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 #include <string>
8 #include <tuple>
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::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     /**
157      * @brief Callback interface implementation that
158      *        creates an error log
159      */
160     void operator()(Context ctx) override
161     {
162         if (ctx == Context::START)
163         {
164             // No action should be taken as this call back is being called from
165             // daemon Startup.
166             return;
167         }
168         auto data = captureMetadata();
169 
170         phosphor::logging::report<errorType>(metadataType(data.c_str()));
171     }
172 
173   private:
174     /**
175      * @brief Builds a metadata string with property information
176      *
177      * Finds all of the properties in the index that have
178      * their condition pass/fail fields (get<resultIndex>(storage))
179      * set to true, and then packs those paths, names, and values
180      * into a metadata string that looks like:
181      *
182      * |path1:name1=value1|path2:name2=value2|...
183      *
184      * @return The metadata string
185      */
186     std::string captureMetadata()
187     {
188         std::string metadata{'|'};
189 
190         for (const auto& n : index)
191         {
192             const auto& storage = std::get<storageIndex>(n.second).get();
193             const auto& result = std::get<resultIndex>(storage);
194 
195             if (result.has_value() && std::any_cast<bool>(result))
196             {
197                 const auto& path = std::get<pathIndex>(n.first).get();
198                 const auto& propertyName =
199                     std::get<propertyIndex>(n.first).get();
200                 auto value = ToString<propertyType>::op(
201                     std::any_cast<propertyType>(std::get<valueIndex>(storage)));
202 
203                 metadata += path + ":" + propertyName + '=' + value + '|';
204             }
205         }
206 
207         return metadata;
208     };
209 };
210 
211 /** @brief Argument type deduction for constructing Elog instances.
212  *
213  *  @tparam T - Error log type
214  *  @tparam Args - Metadata fields types.
215  *  @param[in] arguments - Metadata fields to be added to the error log
216  */
217 template <typename T, typename... Args>
218 auto makeElog(Args&&... arguments)
219 {
220     return std::make_unique<Elog<T, Args...>>(std::forward<Args>(arguments)...);
221 }
222 
223 } // namespace monitoring
224 } // namespace dbus
225 } // namespace phosphor
226