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