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 
9 namespace phosphor
10 {
11 namespace dbus
12 {
13 namespace monitoring
14 {
15 
16 /** @struct ToString
17  * @brief Convert numbers to strings
18  */
19 template <typename T>
20 struct ToString
21 {
22     static auto op(T&& value)
23     {
24         return std::to_string(std::forward<T>(value));
25     }
26 };
27 
28 template <>
29 struct ToString<std::string>
30 {
31     static auto op(const std::string& value)
32     {
33         return value;
34     }
35 };
36 
37 /** @class ElogBase
38  *  @brief Elog callback implementation.
39  *
40  *  The elog callback logs the elog and
41  *  elog metadata.
42  */
43 class ElogBase : public Callback
44 {
45   public:
46     ElogBase(const ElogBase&) = delete;
47     ElogBase(ElogBase&&) = default;
48     ElogBase& operator=(const ElogBase&) = delete;
49     ElogBase& operator=(ElogBase&&) = default;
50     virtual ~ElogBase() = default;
51     ElogBase() : Callback()
52     {
53     }
54 
55     /** @brief Callback interface implementation. */
56     void operator()(Context ctx) override;
57 
58   private:
59     /** @brief Delegate type specific calls to subclasses. */
60     virtual void log() const = 0;
61 };
62 
63 namespace detail
64 {
65 
66 /** @class CallElog
67  *  @brief Provide explicit call forwarding to phosphor::logging::report.
68  *
69  *  @tparam T - Error log type
70  *  @tparam Args - Metadata fields types.
71  */
72 template <typename T, typename... Args>
73 struct CallElog
74 {
75     static void op(Args&&... args)
76     {
77         phosphor::logging::report<T>(std::forward<Args>(args)...);
78     }
79 };
80 
81 } // namespace detail
82 
83 /** @class Elog
84  *  @brief C++ type specific logic for the elog callback.
85  *         The elog callback logs the elog and elog metadata.
86  *
87  *  @tparam T - Error log type
88  *  @tparam Args - Metadata fields types.
89  *  @param[in] arguments - Metadata fields to be added to the error log
90  */
91 template <typename T, typename... Args>
92 class Elog : public ElogBase
93 {
94   public:
95     Elog(const Elog&) = delete;
96     Elog(Elog&&) = default;
97     Elog& operator=(const Elog&) = delete;
98     Elog& operator=(Elog&&) = default;
99     ~Elog() = default;
100     Elog(Args&&... arguments) :
101         ElogBase(), args(std::forward<Args>(arguments)...)
102     {
103     }
104 
105   private:
106     /** @brief elog interface implementation. */
107     void log() const override
108     {
109         std::experimental::apply(detail::CallElog<T, Args...>::op,
110                                  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.empty() && any_ns::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 =
201                     ToString<propertyType>::op(any_ns::any_cast<propertyType>(
202                         std::get<valueIndex>(storage)));
203 
204                 metadata += path + ":" + propertyName + '=' + value + '|';
205             }
206         }
207 
208         return metadata;
209     };
210 };
211 
212 /** @brief Argument type deduction for constructing Elog instances.
213  *
214  *  @tparam T - Error log type
215  *  @tparam Args - Metadata fields types.
216  *  @param[in] arguments - Metadata fields to be added to the error log
217  */
218 template <typename T, typename... Args>
219 auto makeElog(Args&&... arguments)
220 {
221     return std::make_unique<Elog<T, Args...>>(std::forward<Args>(arguments)...);
222 }
223 
224 } // namespace monitoring
225 } // namespace dbus
226 } // namespace phosphor
227