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