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<sdbusplus::exception_t, T>::value,
133                   "T must be a descendant of sdbusplus::exception_t");
134     return details::commit(T::errName);
135 }
136 
137 /** @fn commit()
138  *  @brief Create an error log entry based on journal
139  *         entry with a specified MSG_ID. This override accepts error level.
140  *  @param[in] level - level of the error
141  *
142  *  @return The entry ID
143  */
144 template <typename T>
commit(Entry::Level level)145 uint32_t commit(Entry::Level level)
146 {
147     // Validate if the exception is derived from sdbusplus::exception.
148     static_assert(std::is_base_of<sdbusplus::exception_t, T>::value,
149                   "T must be a descendant of sdbusplus::exception_t");
150     return details::commit(T::errName, level);
151 }
152 
153 /** @fn elog()
154  *  @brief Create a journal log entry based on predefined
155  *          error log information
156  *  @tparam T - Error log type
157  *  @param[in] i_args - Metadata fields to be added to the journal entry
158  */
159 template <typename T, typename... Args>
elog(Args...i_args)160 [[noreturn]] void elog(Args... i_args)
161 {
162     // Validate if the exception is derived from sdbusplus::exception.
163     static_assert(std::is_base_of<sdbusplus::exception_t, T>::value,
164                   "T must be a descendant of sdbusplus::exception_t");
165 
166     // Validate the caller passed in the required parameters
167     static_assert(
168         std::is_same<typename details::map_exception_type_t<T>::metadata_types,
169                      std::tuple<details::deduce_entry_type_t<Args>...>>::value,
170         "You are not passing in required arguments for this error");
171 
172     log<details::map_exception_type_t<T>::L>(
173         T::errDesc, details::deduce_entry_type<Args>{i_args}.get()...);
174 
175     // Now throw an exception for this error
176     throw T();
177 }
178 
179 /** @fn report()
180  *  @brief Create a journal log entry based on predefined
181  *         error log information and commit the error
182  *  @tparam T - exception
183  *  @param[in] i_args - Metadata fields to be added to the journal entry
184  *
185  *  @return The entry ID
186  */
187 template <typename T, typename... Args>
report(Args...i_args)188 uint32_t report(Args... i_args)
189 {
190     // validate if the exception is derived from sdbusplus::exception.
191     static_assert(std::is_base_of<sdbusplus::exception_t, T>::value,
192                   "T must be a descendant of sdbusplus::exception_t");
193 
194     // Validate the caller passed in the required parameters
195     static_assert(
196         std::is_same<typename details::map_exception_type_t<T>::metadata_types,
197                      std::tuple<details::deduce_entry_type_t<Args>...>>::value,
198         "You are not passing in required arguments for this error");
199 
200     log<details::map_exception_type_t<T>::L>(
201         T::errDesc, details::deduce_entry_type<Args>{i_args}.get()...);
202 
203     return commit<T>();
204 }
205 
206 /** @fn report()
207  *  @brief Create a journal log entry based on predefined
208  *         error log information and commit the error. Accepts error
209  *         level.
210  *  @tparam T - exception
211  *  @param[in] level - level of the error
212  *  @param[in] i_args - Metadata fields to be added to the journal entry
213  *
214  *  @return The entry ID
215  */
216 template <typename T, typename... Args>
report(Entry::Level level,Args...i_args)217 uint32_t report(Entry::Level level, Args... i_args)
218 {
219     // validate if the exception is derived from sdbusplus::exception.
220     static_assert(std::is_base_of<sdbusplus::exception_t, T>::value,
221                   "T must be a descendant of sdbusplus::exception_t");
222 
223     // Validate the caller passed in the required parameters
224     static_assert(
225         std::is_same<typename details::map_exception_type_t<T>::metadata_types,
226                      std::tuple<details::deduce_entry_type_t<Args>...>>::value,
227         "You are not passing in required arguments for this error");
228 
229     log<details::map_exception_type_t<T>::L>(
230         T::errDesc, details::deduce_entry_type<Args>{i_args}.get()...);
231 
232     return commit<T>(level);
233 }
234 
235 } // namespace logging
236 
237 } // namespace phosphor
238