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 #include <string> 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::experimental::apply(detail::CallElog<T, Args...>::op, 111 std::tuple_cat(args)); 112 } 113 std::tuple<Args...> args; 114 }; 115 116 /** 117 * @class ElogWithMetadataCapture 118 * 119 * @brief A callback class that will save the paths, names, and 120 * current values of certain properties in the metadata of the 121 * error log it creates. 122 * 123 * The intended use case of this class is to create an error log with 124 * metadata that includes the property names and values that caused 125 * the condition to issue this callback. When the condition ran, it had 126 * set the pass/fail field on each property it checked in the properties' 127 * entries in the Storage array. This class then looks at those pass/fail 128 * fields to see which properties to log. 129 * 130 * Note that it's OK if different conditions and callbacks share the same 131 * properties because everything runs serially, so another condition can't 132 * touch those pass/fail fields until all of the first condition's callbacks 133 * are done. 134 * 135 * This class requires that the error log created only have 1 metadata field, 136 * and it must take a string. 137 * 138 * @tparam errorType - Error log type 139 * @tparam metadataType - The metadata to use 140 * @tparam propertyType - The data type of the captured properties 141 */ 142 template <typename errorType, typename metadataType, typename propertyType> 143 class ElogWithMetadataCapture : public IndexedCallback 144 { 145 public: 146 ElogWithMetadataCapture() = delete; 147 ElogWithMetadataCapture(const ElogWithMetadataCapture&) = delete; 148 ElogWithMetadataCapture(ElogWithMetadataCapture&&) = default; 149 ElogWithMetadataCapture& operator=(const ElogWithMetadataCapture&) = delete; 150 ElogWithMetadataCapture& operator=(ElogWithMetadataCapture&&) = default; 151 virtual ~ElogWithMetadataCapture() = default; 152 explicit ElogWithMetadataCapture(const PropertyIndex& index) : 153 IndexedCallback(index) 154 { 155 } 156 157 /** 158 * @brief Callback interface implementation that 159 * creates an error log 160 */ 161 void operator()(Context ctx) override 162 { 163 if (ctx == Context::START) 164 { 165 // No action should be taken as this call back is being called from 166 // daemon Startup. 167 return; 168 } 169 auto data = captureMetadata(); 170 171 phosphor::logging::report<errorType>(metadataType(data.c_str())); 172 } 173 174 private: 175 /** 176 * @brief Builds a metadata string with property information 177 * 178 * Finds all of the properties in the index that have 179 * their condition pass/fail fields (get<resultIndex>(storage)) 180 * set to true, and then packs those paths, names, and values 181 * into a metadata string that looks like: 182 * 183 * |path1:name1=value1|path2:name2=value2|... 184 * 185 * @return The metadata string 186 */ 187 std::string captureMetadata() 188 { 189 std::string metadata{'|'}; 190 191 for (const auto& n : index) 192 { 193 const auto& storage = std::get<storageIndex>(n.second).get(); 194 const auto& result = std::get<resultIndex>(storage); 195 196 if (!result.empty() && any_ns::any_cast<bool>(result)) 197 { 198 const auto& path = std::get<pathIndex>(n.first).get(); 199 const auto& propertyName = 200 std::get<propertyIndex>(n.first).get(); 201 auto value = 202 ToString<propertyType>::op(any_ns::any_cast<propertyType>( 203 std::get<valueIndex>(storage))); 204 205 metadata += path + ":" + propertyName + '=' + value + '|'; 206 } 207 } 208 209 return metadata; 210 }; 211 }; 212 213 /** @brief Argument type deduction for constructing Elog instances. 214 * 215 * @tparam T - Error log type 216 * @tparam Args - Metadata fields types. 217 * @param[in] arguments - Metadata fields to be added to the error log 218 */ 219 template <typename T, typename... Args> 220 auto makeElog(Args&&... arguments) 221 { 222 return std::make_unique<Elog<T, Args...>>(std::forward<Args>(arguments)...); 223 } 224 225 } // namespace monitoring 226 } // namespace dbus 227 } // namespace phosphor 228