mirror of
https://github.com/XRPLF/clio.git
synced 2025-11-04 11:55:51 +00:00
279 lines
8.8 KiB
C++
279 lines
8.8 KiB
C++
//------------------------------------------------------------------------------
|
|
/*
|
|
This file is part of clio: https://github.com/XRPLF/clio
|
|
Copyright (c) 2022, the clio developers.
|
|
|
|
Permission to use, copy, modify, and distribute this software for any
|
|
purpose with or without fee is hereby granted, provided that the above
|
|
copyright notice and this permission notice appear in all copies.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
//==============================================================================
|
|
|
|
#pragma once
|
|
|
|
#include <util/SourceLocation.h>
|
|
|
|
#include <boost/algorithm/string/predicate.hpp>
|
|
#include <boost/filesystem.hpp>
|
|
#include <boost/json.hpp>
|
|
#include <boost/log/core/core.hpp>
|
|
#include <boost/log/expressions/predicates/channel_severity_filter.hpp>
|
|
#include <boost/log/sinks/unlocked_frontend.hpp>
|
|
#include <boost/log/sources/record_ostream.hpp>
|
|
#include <boost/log/sources/severity_channel_logger.hpp>
|
|
#include <boost/log/sources/severity_feature.hpp>
|
|
#include <boost/log/sources/severity_logger.hpp>
|
|
#include <boost/log/utility/manipulators/add_value.hpp>
|
|
#include <boost/log/utility/setup/common_attributes.hpp>
|
|
#include <boost/log/utility/setup/console.hpp>
|
|
#include <boost/log/utility/setup/file.hpp>
|
|
#include <boost/log/utility/setup/formatter_parser.hpp>
|
|
|
|
namespace util {
|
|
|
|
class Config;
|
|
|
|
/**
|
|
* @brief Skips evaluation of expensive argument lists if the given logger is disabled for the required severity level.
|
|
*
|
|
* Note: Currently this introduces potential shadowing (unlikely).
|
|
*/
|
|
#ifndef COVERAGE_ENABLED
|
|
#define LOG(x) \
|
|
if (auto clio_pump__ = x; not clio_pump__) { \
|
|
} else \
|
|
clio_pump__
|
|
#else
|
|
#define LOG(x) x
|
|
#endif
|
|
|
|
/**
|
|
* @brief Custom severity levels for @ref util::Logger.
|
|
*/
|
|
enum class Severity {
|
|
TRC,
|
|
DBG,
|
|
NFO,
|
|
WRN,
|
|
ERR,
|
|
FTL,
|
|
};
|
|
|
|
BOOST_LOG_ATTRIBUTE_KEYWORD(log_severity, "Severity", Severity);
|
|
BOOST_LOG_ATTRIBUTE_KEYWORD(log_channel, "Channel", std::string);
|
|
|
|
/**
|
|
* @brief Custom labels for @ref Severity in log output.
|
|
*
|
|
* @param stream std::ostream The output stream
|
|
* @param sev Severity The severity to output to the ostream
|
|
* @return std::ostream& The same ostream we were given
|
|
*/
|
|
std::ostream&
|
|
operator<<(std::ostream& stream, Severity sev);
|
|
|
|
/**
|
|
* @brief A simple thread-safe logger for the channel specified
|
|
* in the constructor.
|
|
*
|
|
* This is cheap to copy and move. Designed to be used as a member variable or
|
|
* otherwise. See @ref LogService::init() for setup of the logging core and
|
|
* severity levels for each channel.
|
|
*/
|
|
class Logger final {
|
|
using LoggerType = boost::log::sources::severity_channel_logger_mt<Severity, std::string>;
|
|
mutable LoggerType logger_;
|
|
|
|
friend class LogService; // to expose the Pump interface
|
|
|
|
/**
|
|
* @brief Helper that pumps data into a log record via `operator<<`.
|
|
*/
|
|
class Pump final {
|
|
using PumpOptType = std::optional<boost::log::aux::record_pump<LoggerType>>;
|
|
|
|
boost::log::record rec_;
|
|
PumpOptType pump_ = std::nullopt;
|
|
|
|
public:
|
|
~Pump() = default;
|
|
Pump(LoggerType& logger, Severity sev, SourceLocationType const& loc)
|
|
: rec_{logger.open_record(boost::log::keywords::severity = sev)}
|
|
{
|
|
if (rec_) {
|
|
pump_.emplace(boost::log::aux::make_record_pump(logger, rec_));
|
|
pump_->stream() << boost::log::add_value("SourceLocation", pretty_path(loc));
|
|
}
|
|
}
|
|
|
|
Pump(Pump&&) = delete;
|
|
Pump(Pump const&) = delete;
|
|
Pump&
|
|
operator=(Pump const&) = delete;
|
|
Pump&
|
|
operator=(Pump&&) = delete;
|
|
|
|
/**
|
|
* @brief Perfectly forwards any incoming data into the underlying boost::log pump if the pump is available.
|
|
*
|
|
* @tparam T Type of data to pump
|
|
* @param data The data to pump
|
|
* @return Pump& Reference to itself for chaining
|
|
*/
|
|
template <typename T>
|
|
[[maybe_unused]] Pump&
|
|
operator<<(T&& data)
|
|
{
|
|
if (pump_)
|
|
pump_->stream() << std::forward<T>(data);
|
|
return *this;
|
|
}
|
|
|
|
/**
|
|
* @return true if logger is enabled; false otherwise
|
|
*/
|
|
operator bool() const
|
|
{
|
|
return pump_.has_value();
|
|
}
|
|
|
|
private:
|
|
[[nodiscard]] static std::string
|
|
pretty_path(SourceLocationType const& loc, size_t max_depth = 3);
|
|
|
|
/**
|
|
* @brief Custom JSON parser for @ref Severity.
|
|
*
|
|
* @param value The JSON string to parse
|
|
* @return Severity The parsed severity
|
|
* @throws std::runtime_error Thrown if severity is not in the right format
|
|
*/
|
|
friend Severity
|
|
tag_invoke(boost::json::value_to_tag<Severity>, boost::json::value const& value);
|
|
};
|
|
|
|
public:
|
|
~Logger() = default;
|
|
/**
|
|
* @brief Construct a new Logger object that produces loglines for the
|
|
* specified channel.
|
|
*
|
|
* See @ref LogService::init() for general setup and configuration of
|
|
* severity levels per channel.
|
|
*
|
|
* @param channel The channel this logger will report into.
|
|
*/
|
|
Logger(std::string channel) : logger_{boost::log::keywords::channel = channel}
|
|
{
|
|
}
|
|
Logger(Logger const&) = default;
|
|
Logger(Logger&&) = default;
|
|
Logger&
|
|
operator=(Logger const&) = default;
|
|
Logger&
|
|
operator=(Logger&&) = default;
|
|
|
|
/** Interface for logging at Severity::TRC severity */
|
|
[[nodiscard]] Pump
|
|
trace(SourceLocationType const& loc = CURRENT_SRC_LOCATION) const;
|
|
|
|
/** Interface for logging at Severity::DBG severity */
|
|
[[nodiscard]] Pump
|
|
debug(SourceLocationType const& loc = CURRENT_SRC_LOCATION) const;
|
|
|
|
/** Interface for logging at Severity::INFO severity */
|
|
[[nodiscard]] Pump
|
|
info(SourceLocationType const& loc = CURRENT_SRC_LOCATION) const;
|
|
|
|
/** Interface for logging at Severity::WRN severity */
|
|
[[nodiscard]] Pump
|
|
warn(SourceLocationType const& loc = CURRENT_SRC_LOCATION) const;
|
|
|
|
/** Interface for logging at Severity::ERR severity */
|
|
[[nodiscard]] Pump
|
|
error(SourceLocationType const& loc = CURRENT_SRC_LOCATION) const;
|
|
|
|
/** Interface for logging at Severity::FTL severity */
|
|
[[nodiscard]] Pump
|
|
fatal(SourceLocationType const& loc = CURRENT_SRC_LOCATION) const;
|
|
};
|
|
|
|
/**
|
|
* @brief A global logging service.
|
|
*
|
|
* Used to initialize and setup the logging core as well as a globally available
|
|
* entrypoint for logging into the `General` channel as well as raising alerts.
|
|
*/
|
|
class LogService {
|
|
static Logger general_log_; /*< Global logger for General channel */
|
|
static Logger alert_log_; /*< Global logger for Alerts channel */
|
|
|
|
public:
|
|
LogService() = delete;
|
|
|
|
/**
|
|
* @brief Global log core initialization from a @ref Config
|
|
*/
|
|
static void
|
|
init(Config const& config);
|
|
|
|
/** Globally accesible General logger at Severity::TRC severity */
|
|
[[nodiscard]] static Logger::Pump
|
|
trace(SourceLocationType const& loc = CURRENT_SRC_LOCATION)
|
|
{
|
|
return general_log_.trace(loc);
|
|
}
|
|
|
|
/** Globally accesible General logger at Severity::DBG severity */
|
|
[[nodiscard]] static Logger::Pump
|
|
debug(SourceLocationType const& loc = CURRENT_SRC_LOCATION)
|
|
{
|
|
return general_log_.debug(loc);
|
|
}
|
|
|
|
/** Globally accesible General logger at Severity::NFO severity */
|
|
[[nodiscard]] static Logger::Pump
|
|
info(SourceLocationType const& loc = CURRENT_SRC_LOCATION)
|
|
{
|
|
return general_log_.info(loc);
|
|
}
|
|
|
|
/** Globally accesible General logger at Severity::WRN severity */
|
|
[[nodiscard]] static Logger::Pump
|
|
warn(SourceLocationType const& loc = CURRENT_SRC_LOCATION)
|
|
{
|
|
return general_log_.warn(loc);
|
|
}
|
|
|
|
/** Globally accesible General logger at Severity::ERR severity */
|
|
[[nodiscard]] static Logger::Pump
|
|
error(SourceLocationType const& loc = CURRENT_SRC_LOCATION)
|
|
{
|
|
return general_log_.error(loc);
|
|
}
|
|
|
|
/** Globally accesible General logger at Severity::FTL severity */
|
|
[[nodiscard]] static Logger::Pump
|
|
fatal(SourceLocationType const& loc = CURRENT_SRC_LOCATION)
|
|
{
|
|
return general_log_.fatal(loc);
|
|
}
|
|
|
|
/** Globally accesible Alert logger */
|
|
[[nodiscard]] static Logger::Pump
|
|
alert(SourceLocationType const& loc = CURRENT_SRC_LOCATION)
|
|
{
|
|
return alert_log_.warn(loc);
|
|
}
|
|
};
|
|
|
|
}; // namespace util
|