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 {
opphosphor::dbus::monitoring::ToString25     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 {
opphosphor::dbus::monitoring::ToString34     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;
ElogBase()54     ElogBase() : Callback() {}
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 {
opphosphor::dbus::monitoring::detail::CallElog76     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;
Elog(Args &&...arguments)101     explicit Elog(Args&&... arguments) :
102         ElogBase(), args(std::forward<Args>(arguments)...)
103     {}
104 
105   private:
106     /** @brief elog interface implementation. */
log() const107     void log() const override
108     {
109         std::apply(detail::CallElog<T, Args...>::op, std::tuple_cat(args));
110     }
111     std::tuple<Args...> args;
112 };
113 
114 /**
115  * @class ElogWithMetadataCapture
116  *
117  * @brief A callback class that will save the paths, names, and
118  *       current values of certain properties in the metadata of the
119  *       error log it creates.
120  *
121  * The intended use case of this class is to create an error log with
122  * metadata that includes the property names and values that caused
123  * the condition to issue this callback.  When the condition ran, it had
124  * set the pass/fail field on each property it checked in the properties'
125  * entries in the Storage array.  This class then looks at those pass/fail
126  * fields to see which properties to log.
127  *
128  * Note that it's OK if different conditions and callbacks share the same
129  * properties because everything runs serially, so another condition can't
130  * touch those pass/fail fields until all of the first condition's callbacks
131  * are done.
132  *
133  * This class requires that the error log created only have 1 metadata field,
134  * and it must take a string.
135  *
136  * @tparam errorType - Error log type
137  * @tparam metadataType - The metadata to use
138  * @tparam propertyType - The data type of the captured properties
139  */
140 template <typename errorType, typename metadataType, typename propertyType>
141 class ElogWithMetadataCapture : public IndexedCallback
142 {
143   public:
144     ElogWithMetadataCapture() = delete;
145     ElogWithMetadataCapture(const ElogWithMetadataCapture&) = delete;
146     ElogWithMetadataCapture(ElogWithMetadataCapture&&) = default;
147     ElogWithMetadataCapture& operator=(const ElogWithMetadataCapture&) = delete;
148     ElogWithMetadataCapture& operator=(ElogWithMetadataCapture&&) = default;
149     virtual ~ElogWithMetadataCapture() = default;
ElogWithMetadataCapture(const PropertyIndex & index)150     explicit ElogWithMetadataCapture(const PropertyIndex& index) :
151         IndexedCallback(index)
152     {}
153 
154     /**
155      * @brief Callback interface implementation that
156      *        creates an error log
157      */
operator ()(Context ctx)158     void operator()(Context ctx) override
159     {
160         if (ctx == Context::START)
161         {
162             // No action should be taken as this call back is being called from
163             // daemon Startup.
164             return;
165         }
166         auto data = captureMetadata();
167 
168         phosphor::logging::report<errorType>(metadataType(data.c_str()));
169     }
170 
171   private:
172     /**
173      * @brief Builds a metadata string with property information
174      *
175      * Finds all of the properties in the index that have
176      * their condition pass/fail fields (get<resultIndex>(storage))
177      * set to true, and then packs those paths, names, and values
178      * into a metadata string that looks like:
179      *
180      * |path1:name1=value1|path2:name2=value2|...
181      *
182      * @return The metadata string
183      */
captureMetadata()184     std::string captureMetadata()
185     {
186         std::string metadata{'|'};
187 
188         for (const auto& n : index)
189         {
190             const auto& storage = std::get<storageIndex>(n.second).get();
191             const auto& result = std::get<resultIndex>(storage);
192 
193             if (result.has_value() && std::any_cast<bool>(result))
194             {
195                 const auto& path = std::get<pathIndex>(n.first).get();
196                 const auto& propertyName =
197                     std::get<propertyIndex>(n.first).get();
198                 auto value = ToString<propertyType>::op(
199                     std::any_cast<propertyType>(std::get<valueIndex>(storage)));
200 
201                 metadata += path + ":" + propertyName + '=' + value + '|';
202             }
203         }
204 
205         return metadata;
206     };
207 };
208 
209 /** @brief Argument type deduction for constructing Elog instances.
210  *
211  *  @tparam T - Error log type
212  *  @tparam Args - Metadata fields types.
213  *  @param[in] arguments - Metadata fields to be added to the error log
214  */
215 template <typename T, typename... Args>
makeElog(Args &&...arguments)216 auto makeElog(Args&&... arguments)
217 {
218     return std::make_unique<Elog<T, Args...>>(std::forward<Args>(arguments)...);
219 }
220 
221 } // namespace monitoring
222 } // namespace dbus
223 } // namespace phosphor
224