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 { 25 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 { 34 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; 54 ElogBase() : Callback() 55 {} 56 57 /** @brief Callback interface implementation. */ 58 void operator()(Context ctx) override; 59 60 private: 61 /** @brief Delegate type specific calls to subclasses. */ 62 virtual void log() const = 0; 63 }; 64 65 namespace detail 66 { 67 68 /** @class CallElog 69 * @brief Provide explicit call forwarding to phosphor::logging::report. 70 * 71 * @tparam T - Error log type 72 * @tparam Args - Metadata fields types. 73 */ 74 template <typename T, typename... Args> 75 struct CallElog 76 { 77 static void op(Args&&... args) 78 { 79 phosphor::logging::report<T>(std::forward<Args>(args)...); 80 } 81 }; 82 83 } // namespace detail 84 85 /** @class Elog 86 * @brief C++ type specific logic for the elog callback. 87 * The elog callback logs the elog and elog metadata. 88 * 89 * @tparam T - Error log type 90 * @tparam Args - Metadata fields types. 91 * @param[in] arguments - Metadata fields to be added to the error log 92 */ 93 template <typename T, typename... Args> 94 class Elog : public ElogBase 95 { 96 public: 97 Elog(const Elog&) = delete; 98 Elog(Elog&&) = default; 99 Elog& operator=(const Elog&) = delete; 100 Elog& operator=(Elog&&) = default; 101 ~Elog() = default; 102 Elog(Args&&... arguments) : 103 ElogBase(), args(std::forward<Args>(arguments)...) 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 * @brief Callback interface implementation that 157 * creates an error log 158 */ 159 void operator()(Context ctx) override 160 { 161 if (ctx == Context::START) 162 { 163 // No action should be taken as this call back is being called from 164 // daemon Startup. 165 return; 166 } 167 auto data = captureMetadata(); 168 169 phosphor::logging::report<errorType>(metadataType(data.c_str())); 170 } 171 172 private: 173 /** 174 * @brief Builds a metadata string with property information 175 * 176 * Finds all of the properties in the index that have 177 * their condition pass/fail fields (get<resultIndex>(storage)) 178 * set to true, and then packs those paths, names, and values 179 * into a metadata string that looks like: 180 * 181 * |path1:name1=value1|path2:name2=value2|... 182 * 183 * @return The metadata string 184 */ 185 std::string captureMetadata() 186 { 187 std::string metadata{'|'}; 188 189 for (const auto& n : index) 190 { 191 const auto& storage = std::get<storageIndex>(n.second).get(); 192 const auto& result = std::get<resultIndex>(storage); 193 194 if (result.has_value() && std::any_cast<bool>(result)) 195 { 196 const auto& path = std::get<pathIndex>(n.first).get(); 197 const auto& propertyName = 198 std::get<propertyIndex>(n.first).get(); 199 auto value = ToString<propertyType>::op( 200 std::any_cast<propertyType>(std::get<valueIndex>(storage))); 201 202 metadata += path + ":" + propertyName + '=' + value + '|'; 203 } 204 } 205 206 return metadata; 207 }; 208 }; 209 210 /** @brief Argument type deduction for constructing Elog instances. 211 * 212 * @tparam T - Error log type 213 * @tparam Args - Metadata fields types. 214 * @param[in] arguments - Metadata fields to be added to the error log 215 */ 216 template <typename T, typename... Args> 217 auto makeElog(Args&&... arguments) 218 { 219 return std::make_unique<Elog<T, Args...>>(std::forward<Args>(arguments)...); 220 } 221 222 } // namespace monitoring 223 } // namespace dbus 224 } // namespace phosphor 225