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