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