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