blob: 14c0faa708d07c9f1f07d3c4efcf0fd1ae5b310b [file] [log] [blame]
/**
* Copyright © 2016 IBM Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <systemd/sd-journal.h>
#include <phosphor-logging/sdjournal.hpp>
#include <sdbusplus/server/transaction.hpp>
#include <tuple>
#include <type_traits>
namespace phosphor
{
namespace logging
{
/** @enum level
* @brief Enum for priority level
*/
enum class level
{
EMERG = LOG_EMERG,
ALERT = LOG_ALERT,
CRIT = LOG_CRIT,
ERR = LOG_ERR,
WARNING = LOG_WARNING,
NOTICE = LOG_NOTICE,
INFO = LOG_INFO,
DEBUG = LOG_DEBUG,
};
namespace details
{
/** @fn prio()
* @brief Prepend PRIORITY= to the input priority string.
* This is required by sd_journal_send().
* @tparam L - Priority level
*/
template <level L>
constexpr auto prio()
{
constexpr const char* prio_str = "PRIORITY=%d";
constexpr const auto prio_tuple = std::make_tuple(prio_str, L);
return prio_tuple;
}
/** @fn helper_log()
* @brief Helper function for details::log(). Log request to journal.
* @tparam T - Type of tuple
* @tparam I - std::integer_sequence of indexes (0..N) for each tuple element
* @param[in] e - Tuple containing the data to be logged
* @param[unnamed] - std::integer_sequence of tuple's index values
*/
template <typename T, size_t... I>
void helper_log(T&& e, std::integer_sequence<size_t, I...>)
{
// https://www.freedesktop.org/software/systemd/man/sd_journal_print.html
// TODO: Re-enable call through interface for testing (or move the code
// into the body of the object).
sd_journal_send(std::get<I>(std::forward<T>(e))..., NULL);
}
/** @fn details::log()
* @brief Implementation of logging::log() function.
* Send request msg and size to helper funct to log it to the journal.
* @tparam T - Type of tuple
* @param[in] e - Tuple containing the data to be logged
*/
template <typename T>
void log(T&& e)
{
constexpr auto e_size = std::tuple_size<std::decay_t<T>>::value;
helper_log(std::forward<T>(e), std::make_index_sequence<e_size>{});
}
} // namespace details
template <class T>
struct is_char_ptr_argtype
: std::integral_constant<
bool,
(std::is_pointer<typename std::decay<T>::type>::value &&
std::is_same<typename std::remove_cv<typename std::remove_pointer<
typename std::decay<T>::type>::type>::type,
char>::value)>
{
};
template <class T>
struct is_printf_argtype
: std::integral_constant<
bool,
(std::is_integral<typename std::remove_reference<T>::type>::value ||
std::is_enum<typename std::remove_reference<T>::type>::value ||
std::is_floating_point<
typename std::remove_reference<T>::type>::value ||
std::is_pointer<typename std::decay<T>::type>::value)>
{
};
template <bool...>
struct bool_pack;
template <bool... bs>
using all_true = std::is_same<bool_pack<bs..., true>, bool_pack<true, bs...>>;
/** @fn entry()
* @brief Pack each format string entry as a tuple to be able to validate
* the string and parameters when multiple entries are passed to be logged.
* @tparam Arg - Types of first argument
* @tparam Args - Types of remaining arguments
* @param[in] arg - First metadata string of form VAR=value where
* VAR is the variable name in uppercase and
* value is of any size and format
* @param[in] args - Remaining metadata strings
*/
template <typename Arg, typename... Args>
constexpr auto entry(Arg&& arg, Args&&... args)
{
static_assert(is_char_ptr_argtype<Arg>::value,
"bad argument type: use char*");
static_assert(all_true<is_printf_argtype<Args>::value...>::value,
"bad argument type: use string.c_str() if needed");
return std::make_tuple(std::forward<Arg>(arg), std::forward<Args>(args)...);
}
/** @fn log()
* @brief Log message to systemd journal
* @tparam L - Priority level
* @param[in] msg - Message to be logged in C-string format
* @param[in] entry - Metadata fields to be added to the message
* @details Usage: log<level::XX>(const char*, entry(*format), entry()...);
* @example log<level::DEBUG>(
* "Simple Example");
* char msg_str[] = "File not found";
* log<level::DEBUG>(
* msg_str,
* entry("MY_METADATA=%s_%x, name, number));
*/
template <level L, typename Msg, typename... Entry>
void log(Msg msg, Entry... e)
{
static_assert((std::is_same<const char*, std::decay_t<Msg>>::value ||
std::is_same<char*, std::decay_t<Msg>>::value),
"First parameter must be a C-string.");
constexpr const char* msg_str = "MESSAGE=%s";
const auto msg_tuple = std::make_tuple(msg_str, std::forward<Msg>(msg));
constexpr auto transactionStr = "TRANSACTION_ID=%lld";
auto transactionId = sdbusplus::server::transaction::get_id();
auto log_tuple = std::tuple_cat(details::prio<L>(), msg_tuple,
entry(transactionStr, transactionId),
std::forward<Entry>(e)...);
details::log(log_tuple);
}
} // namespace logging
} // namespace phosphor