1 #pragma once
2 #include "xyz/openbmc_project/Logging/Entry/server.hpp"
3
4 #include <phosphor-logging/log.hpp>
5 #include <sdbusplus/exception.hpp>
6
7 #include <tuple>
8 #include <utility>
9
10 namespace phosphor
11 {
12
13 namespace logging
14 {
15
16 using namespace sdbusplus::server::xyz::openbmc_project::logging;
17
18 /**
19 * @brief Structure used by callers to indicate they want to use the last value
20 * put in the journal for input parameter.
21 */
22 template <typename T>
23 struct prev_entry
24 {
25 using type = T;
26 };
27
28 namespace details
29 {
30 /**
31 * @brief Used to return the generated tuple for the error code meta data
32 *
33 * The prev_entry (above) and deduce_entry_type structures below are used
34 * to verify at compile time the required parameters have been passed to
35 * the elog interface and then to forward on the appropriate tuple to the
36 * log interface.
37 */
38 template <typename T>
39 struct deduce_entry_type
40 {
41 using type = T;
getphosphor::logging::details::deduce_entry_type42 auto get()
43 {
44 return value._entry;
45 }
46
47 T value;
48 };
49
50 /**
51 * @brief Used to return an empty tuple for prev_entry parameters
52 *
53 * This is done so we can still call the log() interface with the variable
54 * arg parameters to elog. The log() interface will simply ignore the empty
55 * tuples which is what we want for prev_entry parameters.
56 */
57 template <typename T>
58 struct deduce_entry_type<prev_entry<T>>
59 {
60 using type = T;
getphosphor::logging::details::deduce_entry_type61 auto get()
62 {
63 return std::make_tuple();
64 }
65
66 prev_entry<T> value;
67 };
68
69 /**
70 * @brief Typedef for above structure usage
71 */
72 template <typename T>
73 using deduce_entry_type_t = typename deduce_entry_type<T>::type;
74
75 /**
76 * @brief Used to map an sdbusplus error to a phosphor-logging error type
77 *
78 * Users log errors via the sdbusplus error name, and the execption that's
79 * thrown is the corresponding sdbusplus exception. However, there's a need
80 * to map the sdbusplus error name to the phosphor-logging error name, in order
81 * to verify the error metadata at compile-time.
82 */
83 template <typename T>
84 struct map_exception_type
85 {
86 using type = T;
87 };
88
89 /**
90 * @brief Typedef for above structure usage
91 */
92 template <typename T>
93 using map_exception_type_t = typename map_exception_type<T>::type;
94
95 /** @fn commit()
96 * @brief Create an error log entry based on journal
97 * entry with a specified exception name
98 * @param[in] name - name of the error exception
99 *
100 * @return The entry ID
101 */
102 uint32_t commit(const char* name);
103
104 /** @fn commit() - override that accepts error level
105 *
106 * @return The entry ID
107 */
108 uint32_t commit(const char* name, Entry::Level level);
109
110 } // namespace details
111
112 /** @fn commit()
113 * \deprecated use commit<T>()
114 * @brief Create an error log entry based on journal
115 * entry with a specified MSG_ID
116 * @param[in] name - name of the error exception
117 *
118 * @return The entry ID
119 */
120 uint32_t commit(std::string&& name);
121
122 /** @fn commit()
123 * @brief Create an error log entry based on journal
124 * entry with a specified MSG_ID
125 *
126 * @return The entry ID
127 */
128 template <typename T>
commit()129 uint32_t commit()
130 {
131 // Validate if the exception is derived from sdbusplus::exception.
132 static_assert(std::is_base_of_v<sdbusplus::exception_t, T>,
133 "T must be a descendant of sdbusplus::exception_t");
134 static_assert(
135 !std::is_base_of_v<sdbusplus::exception::generated_event<T>, T>,
136 "T must NOT be an sdbusplus::generated_event");
137
138 return details::commit(T::errName);
139 }
140
141 /** @fn commit()
142 * @brief Create an error log entry based on journal
143 * entry with a specified MSG_ID. This override accepts error level.
144 * @param[in] level - level of the error
145 *
146 * @return The entry ID
147 */
148 template <typename T>
commit(Entry::Level level)149 uint32_t commit(Entry::Level level)
150 {
151 // Validate if the exception is derived from sdbusplus::exception.
152 static_assert(std::is_base_of_v<sdbusplus::exception_t, T>,
153 "T must be a descendant of sdbusplus::exception_t");
154 static_assert(
155 !std::is_base_of_v<sdbusplus::exception::generated_event<T>, T>,
156 "T must NOT be an sdbusplus::generated_event");
157
158 return details::commit(T::errName, level);
159 }
160
161 /** @fn elog()
162 * @brief Create a journal log entry based on predefined
163 * error log information
164 * @tparam T - Error log type
165 * @param[in] i_args - Metadata fields to be added to the journal entry
166 */
167 template <typename T, typename... Args>
elog(Args...i_args)168 [[noreturn]] void elog(Args... i_args)
169 {
170 // Validate if the exception is derived from sdbusplus::exception.
171 static_assert(std::is_base_of_v<sdbusplus::exception_t, T>,
172 "T must be a descendant of sdbusplus::exception_t");
173 static_assert(
174 !std::is_base_of_v<sdbusplus::exception::generated_event<T>, T>,
175 "T must NOT be an sdbusplus::generated_event");
176
177 // Validate the caller passed in the required parameters
178 static_assert(std::is_same_v<
179 typename details::map_exception_type_t<T>::metadata_types,
180 std::tuple<details::deduce_entry_type_t<Args>...>>,
181 "You are not passing in required arguments for this error");
182
183 log<details::map_exception_type_t<T>::L>(
184 T::errDesc, details::deduce_entry_type<Args>{i_args}.get()...);
185
186 // Now throw an exception for this error
187 throw T();
188 }
189
190 /** @fn report()
191 * @brief Create a journal log entry based on predefined
192 * error log information and commit the error
193 * @tparam T - exception
194 * @param[in] i_args - Metadata fields to be added to the journal entry
195 *
196 * @return The entry ID
197 */
198 template <typename T, typename... Args>
report(Args...i_args)199 uint32_t report(Args... i_args)
200 {
201 // validate if the exception is derived from sdbusplus::exception.
202 static_assert(std::is_base_of_v<sdbusplus::exception_t, T>,
203 "T must be a descendant of sdbusplus::exception_t");
204 static_assert(
205 !std::is_base_of_v<sdbusplus::exception::generated_event<T>, T>,
206 "T must NOT be an sdbusplus::generated_event");
207
208 // Validate the caller passed in the required parameters
209 static_assert(std::is_same_v<
210 typename details::map_exception_type_t<T>::metadata_types,
211 std::tuple<details::deduce_entry_type_t<Args>...>>,
212 "You are not passing in required arguments for this error");
213
214 log<details::map_exception_type_t<T>::L>(
215 T::errDesc, details::deduce_entry_type<Args>{i_args}.get()...);
216
217 return commit<T>();
218 }
219
220 /** @fn report()
221 * @brief Create a journal log entry based on predefined
222 * error log information and commit the error. Accepts error
223 * level.
224 * @tparam T - exception
225 * @param[in] level - level of the error
226 * @param[in] i_args - Metadata fields to be added to the journal entry
227 *
228 * @return The entry ID
229 */
230 template <typename T, typename... Args>
report(Entry::Level level,Args...i_args)231 uint32_t report(Entry::Level level, Args... i_args)
232 {
233 // validate if the exception is derived from sdbusplus::exception.
234 static_assert(std::is_base_of_v<sdbusplus::exception_t, T>,
235 "T must be a descendant of sdbusplus::exception_t");
236 static_assert(
237 !std::is_base_of_v<sdbusplus::exception::generated_event<T>, T>,
238 "T must NOT be an sdbusplus::generated_event");
239
240 // Validate the caller passed in the required parameters
241 static_assert(std::is_same_v<
242 typename details::map_exception_type_t<T>::metadata_types,
243 std::tuple<details::deduce_entry_type_t<Args>...>>,
244 "You are not passing in required arguments for this error");
245
246 log<details::map_exception_type_t<T>::L>(
247 T::errDesc, details::deduce_entry_type<Args>{i_args}.get()...);
248
249 return commit<T>(level);
250 }
251
252 } // namespace logging
253
254 } // namespace phosphor
255