blob: d98984c1fd05bf186cfe5c9f1b776977f044594c [file] [log] [blame]
#pragma once
#include "callback.hpp"
#include <experimental/tuple>
#include <phosphor-logging/elog-errors.hpp>
#include <phosphor-logging/elog.hpp>
#include <sdbusplus/exception.hpp>
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
{
if (ctx == Context::START)
{
// No action should be taken as this call back is being called from
// daemon Startup.
return;
}
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<resultIndex>(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<storageIndex>(n.second).get();
const auto& result = std::get<resultIndex>(storage);
if (!result.empty() && any_ns::any_cast<bool>(result))
{
const auto& path = std::get<pathIndex>(n.first).get();
const auto& propertyName =
std::get<propertyIndex>(n.first).get();
auto value =
ToString<propertyType>::op(any_ns::any_cast<propertyType>(
std::get<valueIndex>(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