xref: /openbmc/phosphor-logging/lib/include/phosphor-logging/log.hpp (revision 40fb54935ce7367636a7156039396ee91cc4d5e2)
1 // SPDX-License-Identifier: Apache-2.0
2 // SPDX-FileCopyrightText: Copyright 2016 IBM Corporation
3 
4 #pragma once
5 
6 #include <systemd/sd-journal.h>
7 
8 #include <phosphor-logging/sdjournal.hpp>
9 #include <sdbusplus/server/transaction.hpp>
10 
11 #include <tuple>
12 #include <type_traits>
13 
14 namespace phosphor
15 {
16 
17 namespace logging
18 {
19 
20 /** @enum level
21  *  @brief Enum for priority level
22  */
23 enum class level
24 {
25     EMERG = LOG_EMERG,
26     ALERT = LOG_ALERT,
27     CRIT = LOG_CRIT,
28     ERR = LOG_ERR,
29     WARNING = LOG_WARNING,
30     NOTICE = LOG_NOTICE,
31     INFO = LOG_INFO,
32     DEBUG = LOG_DEBUG,
33 };
34 
35 namespace details
36 {
37 
38 /** @fn prio()
39  *  @brief Prepend PRIORITY= to the input priority string.
40  *    This is required by sd_journal_send().
41  *  @tparam L - Priority level
42  */
43 template <level L>
prio()44 constexpr auto prio()
45 {
46     constexpr const char* prio_str = "PRIORITY=%d";
47     constexpr const auto prio_tuple = std::make_tuple(prio_str, L);
48     return prio_tuple;
49 }
50 
51 /** @fn helper_log()
52  *  @brief Helper function for details::log(). Log request to journal.
53  *  @tparam T - Type of tuple
54  *  @tparam I - std::integer_sequence of indexes (0..N) for each tuple element
55  *  @param[in] e - Tuple containing the data to be logged
56  *  @param[unnamed] - std::integer_sequence of tuple's index values
57  */
58 template <typename T, size_t... I>
helper_log(T && e,std::integer_sequence<size_t,I...>)59 void helper_log(T&& e, std::integer_sequence<size_t, I...>)
60 {
61     // https://www.freedesktop.org/software/systemd/man/sd_journal_print.html
62     // TODO: Re-enable call through interface for testing (or move the code
63     // into the body of the object).
64     sd_journal_send(std::get<I>(std::forward<T>(e))..., NULL);
65 }
66 
67 /** @fn details::log()
68  *  @brief Implementation of logging::log() function.
69  *         Send request msg and size to helper funct to log it to the journal.
70  *  @tparam T - Type of tuple
71  *  @param[in] e - Tuple containing the data to be logged
72  */
73 template <typename T>
log(T && e)74 void log(T&& e)
75 {
76     constexpr auto e_size = std::tuple_size<std::decay_t<T>>::value;
77     helper_log(std::forward<T>(e), std::make_index_sequence<e_size>{});
78 }
79 
80 } // namespace details
81 
82 template <class T>
83 struct is_char_ptr_argtype :
84     std::integral_constant<
85         bool,
86         (std::is_pointer<typename std::decay<T>::type>::value &&
87          std::is_same<typename std::remove_cv<typename std::remove_pointer<
88                           typename std::decay<T>::type>::type>::type,
89                       char>::value)>
90 {};
91 
92 template <class T>
93 struct is_printf_argtype :
94     std::integral_constant<
95         bool,
96         (std::is_integral<typename std::remove_reference<T>::type>::value ||
97          std::is_enum<typename std::remove_reference<T>::type>::value ||
98          std::is_floating_point<
99              typename std::remove_reference<T>::type>::value ||
100          std::is_pointer<typename std::decay<T>::type>::value)>
101 {};
102 
103 template <bool...>
104 struct bool_pack;
105 
106 template <bool... bs>
107 using all_true = std::is_same<bool_pack<bs..., true>, bool_pack<true, bs...>>;
108 
109 /** @fn entry()
110  *  @brief Pack each format string entry as a tuple to be able to validate
111  *    the string and parameters when multiple entries are passed to be logged.
112  *  @tparam Arg - Types of first argument
113  *  @tparam Args - Types of remaining arguments
114  *  @param[in] arg - First metadata string of form VAR=value where
115  *    VAR is the variable name in uppercase and
116  *    value is of any size and format
117  *  @param[in] args - Remaining metadata strings
118  */
119 template <typename Arg, typename... Args>
entry(Arg && arg,Args &&...args)120 constexpr auto entry(Arg&& arg, Args&&... args)
121 {
122     static_assert(is_char_ptr_argtype<Arg>::value,
123                   "bad argument type: use char*");
124     static_assert(all_true<is_printf_argtype<Args>::value...>::value,
125                   "bad argument type: use string.c_str() if needed");
126     return std::make_tuple(std::forward<Arg>(arg), std::forward<Args>(args)...);
127 }
128 
129 /** @fn log()
130  *  @brief Log message to systemd journal
131  *  @tparam L - Priority level
132  *  @param[in] msg - Message to be logged in C-string format
133  *  @param[in] entry - Metadata fields to be added to the message
134  *  @details Usage: log<level::XX>(const char*, entry(*format), entry()...);
135  *  @example log<level::DEBUG>(
136  *                 "Simple Example");
137  *           char msg_str[] = "File not found";
138  *           log<level::DEBUG>(
139  *                 msg_str,
140  *                 entry("MY_METADATA=%s_%x, name, number));
141  */
142 template <level L, typename Msg, typename... Entry>
log(Msg msg,Entry...e)143 void log(Msg msg, Entry... e)
144 {
145     static_assert((std::is_same<const char*, std::decay_t<Msg>>::value ||
146                    std::is_same<char*, std::decay_t<Msg>>::value),
147                   "First parameter must be a C-string.");
148 
149     constexpr const char* msg_str = "MESSAGE=%s";
150     const auto msg_tuple = std::make_tuple(msg_str, std::forward<Msg>(msg));
151 
152     constexpr auto transactionStr = "TRANSACTION_ID=%llu";
153     auto transactionId = sdbusplus::server::transaction::get_id();
154 
155     auto log_tuple = std::tuple_cat(details::prio<L>(), msg_tuple,
156                                     entry(transactionStr, transactionId),
157                                     std::forward<Entry>(e)...);
158     details::log(log_tuple);
159 }
160 
161 } // namespace logging
162 
163 } // namespace phosphor
164