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