xref: /openbmc/phosphor-dbus-monitor/src/elog.hpp (revision 3fe976cc22e579860f5b1832d920636d93145507)
1 #pragma once
2 #include "callback.hpp"
3 
4 #include <phosphor-logging/elog-errors.hpp>
5 #include <phosphor-logging/elog.hpp>
6 #include <sdbusplus/exception.hpp>
7 
8 #include <experimental/tuple>
9 #include <string>
10 #include <tuple>
11 
12 namespace phosphor
13 {
14 namespace dbus
15 {
16 namespace monitoring
17 {
18 
19 /** @struct ToString
20  * @brief Convert numbers to strings
21  */
22 template <typename T>
23 struct ToString
24 {
25     static auto op(T&& value)
26     {
27         return std::to_string(std::forward<T>(value));
28     }
29 };
30 
31 template <>
32 struct ToString<std::string>
33 {
34     static auto op(const std::string& value)
35     {
36         return value;
37     }
38 };
39 
40 /** @class ElogBase
41  *  @brief Elog callback implementation.
42  *
43  *  The elog callback logs the elog and
44  *  elog metadata.
45  */
46 class ElogBase : public Callback
47 {
48   public:
49     ElogBase(const ElogBase&) = delete;
50     ElogBase(ElogBase&&) = default;
51     ElogBase& operator=(const ElogBase&) = delete;
52     ElogBase& operator=(ElogBase&&) = default;
53     virtual ~ElogBase() = default;
54     ElogBase() : Callback()
55     {}
56 
57     /** @brief Callback interface implementation. */
58     void operator()(Context ctx) override;
59 
60   private:
61     /** @brief Delegate type specific calls to subclasses. */
62     virtual void log() const = 0;
63 };
64 
65 namespace detail
66 {
67 
68 /** @class CallElog
69  *  @brief Provide explicit call forwarding to phosphor::logging::report.
70  *
71  *  @tparam T - Error log type
72  *  @tparam Args - Metadata fields types.
73  */
74 template <typename T, typename... Args>
75 struct CallElog
76 {
77     static void op(Args&&... args)
78     {
79         phosphor::logging::report<T>(std::forward<Args>(args)...);
80     }
81 };
82 
83 } // namespace detail
84 
85 /** @class Elog
86  *  @brief C++ type specific logic for the elog callback.
87  *         The elog callback logs the elog and elog metadata.
88  *
89  *  @tparam T - Error log type
90  *  @tparam Args - Metadata fields types.
91  *  @param[in] arguments - Metadata fields to be added to the error log
92  */
93 template <typename T, typename... Args>
94 class Elog : public ElogBase
95 {
96   public:
97     Elog(const Elog&) = delete;
98     Elog(Elog&&) = default;
99     Elog& operator=(const Elog&) = delete;
100     Elog& operator=(Elog&&) = default;
101     ~Elog() = default;
102     Elog(Args&&... arguments) :
103         ElogBase(), args(std::forward<Args>(arguments)...)
104     {}
105 
106   private:
107     /** @brief elog interface implementation. */
108     void log() const override
109     {
110         std::apply(detail::CallElog<T, Args...>::op, std::tuple_cat(args));
111     }
112     std::tuple<Args...> args;
113 };
114 
115 /**
116  * @class ElogWithMetadataCapture
117  *
118  * @brief A callback class that will save the paths, names, and
119  *       current values of certain properties in the metadata of the
120  *       error log it creates.
121  *
122  * The intended use case of this class is to create an error log with
123  * metadata that includes the property names and values that caused
124  * the condition to issue this callback.  When the condition ran, it had
125  * set the pass/fail field on each property it checked in the properties'
126  * entries in the Storage array.  This class then looks at those pass/fail
127  * fields to see which properties to log.
128  *
129  * Note that it's OK if different conditions and callbacks share the same
130  * properties because everything runs serially, so another condition can't
131  * touch those pass/fail fields until all of the first condition's callbacks
132  * are done.
133  *
134  * This class requires that the error log created only have 1 metadata field,
135  * and it must take a string.
136  *
137  * @tparam errorType - Error log type
138  * @tparam metadataType - The metadata to use
139  * @tparam propertyType - The data type of the captured properties
140  */
141 template <typename errorType, typename metadataType, typename propertyType>
142 class ElogWithMetadataCapture : public IndexedCallback
143 {
144   public:
145     ElogWithMetadataCapture() = delete;
146     ElogWithMetadataCapture(const ElogWithMetadataCapture&) = delete;
147     ElogWithMetadataCapture(ElogWithMetadataCapture&&) = default;
148     ElogWithMetadataCapture& operator=(const ElogWithMetadataCapture&) = delete;
149     ElogWithMetadataCapture& operator=(ElogWithMetadataCapture&&) = default;
150     virtual ~ElogWithMetadataCapture() = default;
151     explicit ElogWithMetadataCapture(const PropertyIndex& index) :
152         IndexedCallback(index)
153     {}
154 
155     /**
156      * @brief Callback interface implementation that
157      *        creates an error log
158      */
159     void operator()(Context ctx) override
160     {
161         if (ctx == Context::START)
162         {
163             // No action should be taken as this call back is being called from
164             // daemon Startup.
165             return;
166         }
167         auto data = captureMetadata();
168 
169         phosphor::logging::report<errorType>(metadataType(data.c_str()));
170     }
171 
172   private:
173     /**
174      * @brief Builds a metadata string with property information
175      *
176      * Finds all of the properties in the index that have
177      * their condition pass/fail fields (get<resultIndex>(storage))
178      * set to true, and then packs those paths, names, and values
179      * into a metadata string that looks like:
180      *
181      * |path1:name1=value1|path2:name2=value2|...
182      *
183      * @return The metadata string
184      */
185     std::string captureMetadata()
186     {
187         std::string metadata{'|'};
188 
189         for (const auto& n : index)
190         {
191             const auto& storage = std::get<storageIndex>(n.second).get();
192             const auto& result = std::get<resultIndex>(storage);
193 
194             if (result.has_value() && std::any_cast<bool>(result))
195             {
196                 const auto& path = std::get<pathIndex>(n.first).get();
197                 const auto& propertyName =
198                     std::get<propertyIndex>(n.first).get();
199                 auto value = ToString<propertyType>::op(
200                     std::any_cast<propertyType>(std::get<valueIndex>(storage)));
201 
202                 metadata += path + ":" + propertyName + '=' + value + '|';
203             }
204         }
205 
206         return metadata;
207     };
208 };
209 
210 /** @brief Argument type deduction for constructing Elog instances.
211  *
212  *  @tparam T - Error log type
213  *  @tparam Args - Metadata fields types.
214  *  @param[in] arguments - Metadata fields to be added to the error log
215  */
216 template <typename T, typename... Args>
217 auto makeElog(Args&&... arguments)
218 {
219     return std::make_unique<Elog<T, Args...>>(std::forward<Args>(arguments)...);
220 }
221 
222 } // namespace monitoring
223 } // namespace dbus
224 } // namespace phosphor
225