blob: 3f8ae3b8bb2bd52aa91b28ff3785431aa5ad6f22 [file] [log] [blame]
#pragma once
#include <phosphor-logging/elog-errors.hpp>
#include <phosphor-logging/elog.hpp>
#include "callback.hpp"
#include <sdbusplus/exception.hpp>
#include <experimental/tuple>
namespace phosphor
{
namespace dbus
{
namespace monitoring
{
/** @struct ToString
* @brief Convert numbers to strings
*/
template <typename T> struct ToString
{
static auto op(T&& value)
{
return std::to_string(std::forward<T>(value));
}
};
template <> struct ToString<std::string>
{
static auto op(const std::string& value)
{
return value;
}
};
/** @class ElogBase
* @brief Elog callback implementation.
*
* The elog callback logs the elog and
* elog metadata.
*/
class ElogBase : public Callback
{
public:
ElogBase(const ElogBase&) = delete;
ElogBase(ElogBase&&) = default;
ElogBase& operator=(const ElogBase&) = delete;
ElogBase& operator=(ElogBase&&) = default;
virtual ~ElogBase() = default;
ElogBase() :
Callback() {}
/** @brief Callback interface implementation. */
void operator()(Context ctx) override;
private:
/** @brief Delegate type specific calls to subclasses. */
virtual void log() const = 0;
};
namespace detail
{
/** @class CallElog
* @brief Provide explicit call forwarding to phosphor::logging::report.
*
* @tparam T - Error log type
* @tparam Args - Metadata fields types.
*/
template <typename T, typename ...Args>
struct CallElog
{
static void op(Args&& ...args)
{
phosphor::logging::report<T>(std::forward<Args>(args)...);
}
};
} // namespace detail
/** @class Elog
* @brief C++ type specific logic for the elog callback.
* The elog callback logs the elog and elog metadata.
*
* @tparam T - Error log type
* @tparam Args - Metadata fields types.
* @param[in] arguments - Metadata fields to be added to the error log
*/
template <typename T, typename ...Args>
class Elog : public ElogBase
{
public:
Elog(const Elog&) = delete;
Elog(Elog&&) = default;
Elog& operator=(const Elog&) = delete;
Elog& operator=(Elog&&) = default;
~Elog() = default;
Elog(Args&& ... arguments) :
ElogBase(), args(std::forward<Args>(arguments)...) {}
private:
/** @brief elog interface implementation. */
void log() const override
{
std::experimental::apply(
detail::CallElog<T, Args...>::op,
std::tuple_cat(args));
}
std::tuple<Args...> args;
};
/**
* @class ElogWithMetadataCapture
*
* @brief A callback class that will save the paths, names, and
* current values of certain properties in the metadata of the
* error log it creates.
*
* The intended use case of this class is to create an error log with
* metadata that includes the property names and values that caused
* the condition to issue this callback. When the condition ran, it had
* set the pass/fail field on each property it checked in the properties'
* entries in the Storage array. This class then looks at those pass/fail
* fields to see which properties to log.
*
* Note that it's OK if different conditions and callbacks share the same
* properties because everything runs serially, so another condition can't
* touch those pass/fail fields until all of the first condition's callbacks
* are done.
*
* This class requires that the error log created only have 1 metadata field,
* and it must take a string.
*
* @tparam errorType - Error log type
* @tparam metadataType - The metadata to use
* @tparam propertyType - The data type of the captured properties
*/
template<typename errorType,
typename metadataType,
typename propertyType>
class ElogWithMetadataCapture : public IndexedCallback
{
public:
ElogWithMetadataCapture() = delete;
ElogWithMetadataCapture(const ElogWithMetadataCapture&) = delete;
ElogWithMetadataCapture(ElogWithMetadataCapture&&) = default;
ElogWithMetadataCapture& operator=(
const ElogWithMetadataCapture&) = delete;
ElogWithMetadataCapture& operator=(
ElogWithMetadataCapture&&) = default;
virtual ~ElogWithMetadataCapture() = default;
explicit ElogWithMetadataCapture(
const PropertyIndex& index) :
IndexedCallback(index) {}
/**
* @brief Callback interface implementation that
* creates an error log
*/
void operator()(Context ctx) override
{
auto data = captureMetadata();
phosphor::logging::report<errorType>(
metadataType(data.c_str()));
}
private:
/**
* @brief Builds a metadata string with property information
*
* Finds all of the properties in the index that have
* their condition pass/fail fields (get<1>(storage))
* set to true, and then packs those paths, names, and values
* into a metadata string that looks like:
*
* |path1:name1=value1|path2:name2=value2|...
*
* @return The metadata string
*/
std::string captureMetadata()
{
std::string metadata{'|'};
for (const auto& n : index)
{
const auto& storage = std::get<2>(n.second).get();
const auto& result = std::get<1>(storage);
if (!result.empty() && any_ns::any_cast<bool>(result))
{
const auto& path = std::get<0>(n.first).get();
const auto& propertyName = std::get<2>(n.first).get();
auto value = ToString<propertyType>::op(
any_ns::any_cast<propertyType>(
std::get<0>(storage)));
metadata += path + ":" + propertyName +
'=' + value + '|';
}
}
return metadata;
};
};
/** @brief Argument type deduction for constructing Elog instances.
*
* @tparam T - Error log type
* @tparam Args - Metadata fields types.
* @param[in] arguments - Metadata fields to be added to the error log
*/
template <typename T, typename ...Args>
auto makeElog(Args&& ... arguments)
{
return std::make_unique<Elog<T, Args...>>(
std::forward<Args>(arguments)...);
}
} // namespace monitoring
} // namespace dbus
} // namespace phosphor