easylogging++的那些事(四)源码分析(五)日志格式配置方式

在上一篇我们分析了 日志格式配置管理类,结尾的时候我们说过 easylogging++提供了多种日志格式配置的方式,今天我们就来一一看看这些配置方式。

通过加载配置文件

Configurations 类支持从配置文件中加载配置

    对应接口声明如下:

Configurations(const std::string& configurationFile, bool useDefaultsForRemaining = true, Configurations* base = nullptr);

    Configurations 类在 日志格式配置管理类 中已经介绍过了。
    TypedConfigurations 类支持 Configurations 来构造。对应接口声明如下:

TypedConfigurations(Configurations* configurations, LogStreamsReferenceMapPtr logStreamsReference);

    TypedConfigurations 类在 日志格式配置管理类 中已经介绍过了。

Logger 类提供了配置相关的接口。

Logger 类支持通过 configurations 类来配置

void Logger::configure(const Configurations &configurations)
{
    m_isConfigured = false; // we set it to false in case if we fail
    // 初始化日志记录器的所有日志级别对应的日志文件的未刷新次数
    initUnflushedCount();
    if (m_typedConfigurations != nullptr)
    {
        Configurations *c = const_cast<Configurations *>(m_typedConfigurations->configurations());
        if (c->hasConfiguration(Level::Global, ConfigurationType::Filename))
        {
            // 配置了日志文件则刷新文件
            flush();
        }
    }
    base::threading::ScopedLock scopedLock(lock());
    // 用于设置的configurations对象不是Logger实例当前保存的configurations对象
    if (m_configurations != configurations)
    {
        // 重置Logger实例的基准配置
        m_configurations.setFromBase(const_cast<Configurations *>(&configurations));
    }
    // 重建m_typedConfigurations对象
    base::utils::safeDelete(m_typedConfigurations);
    m_typedConfigurations = new base::TypedConfigurations(&m_configurations, m_logStreamsReference);
    // 将所有日志级别的配置当中的日志格式字符串(FORMAT配置项字符串形式的值)当中的日志记录器指示符%logger替换为实际的logger id
    resolveLoggerFormatSpec();
    m_isConfigured = true;
}

    resolveLoggerFormatSpec 的实现如下:

void Logger::resolveLoggerFormatSpec(void) const
{
    base::type::EnumType lIndex = LevelHelper::kMinValid;
    LevelHelper::forEachLevel(&lIndex, [&](void) -> bool
                              {
    // 指定日志级别对应的获取FORMAT配置项对应的base::LogFormat实例,base::LogFormat类用于管理日志格式当中的FORMAT配置项,一个FORMAT配置项对应一个base::LogFormat类实例。   
    base::LogFormat* logFormat =
    const_cast<base::LogFormat*>(&m_typedConfigurations->logFormat(LevelHelper::castFromInt(lIndex)));
    // FORMAT配置项字符串形式的值当中的日志记录器指示符%logger替换为实际的logger id
    base::utils::Str::replaceFirstWithEscape(logFormat->m_format, base::consts::kLoggerIdFormatSpecifier, m_id);
    return false; });
}

    LevelHelper::forEachLevel 接口在 CLOG其他相关类 中已经介绍过了:从指定日志级别开始遍历,对于每个级别执行一些操作(fn)。
    initUnflushedCount 接口和 flush 接口在 CLOG其他相关类 中已经详细介绍过了,这里就不多说了。
    Configurations 类和 TypedConfigurations 类在 日志格式配置管理类 中已经介绍过了。
    base::LogFormat 类在会面的文章中会专门进行介绍,这里就不多说了。

Logger 类支持从 Configurations 类来构造

Logger::Logger(const std::string &id, const Configurations &configurations, base::LogStreamsReferenceMapPtr logStreamsReference)
    : m_id(id), m_typedConfigurations(nullptr), m_parentApplicationName(std::string()), m_isConfigured(false), m_logStreamsReference(logStreamsReference)
{
    initUnflushedCount();
    configure(configurations);
}

    Loggerconfigure 接口在前面已经介绍过了。
    initUnflushedCount 接口在 CLOG其他相关类 中已经详细介绍过了,这里就不多说了。

Logger 支持基于现有配置重新配置

void Logger::reconfigure(void)
{
    ELPP_INTERNAL_INFO(1, "Reconfiguring logger [" << m_id << "]");
    configure(m_configurations);
}

    Loggerconfigure 接口在前面已经介绍过了。

通过相应的 API 以字符串形式来配置

    Configurations 类支持以字符串形式加载配置,相应接口声明如下:

bool parseFromText(const std::string& configurationsString, Configurations* base = nullptr);

    Configurations 类相关的接口在 日志格式配置管理类 中已经介绍过了。

通过命令行参数来配置

    START_EASYLOGGINGPP 宏就是 easylogging++用来解析命令行参数的。
    START_EASYLOGGINGPP 宏定义如下:

#if defined(ELPP_UNICODE)
#define START_EASYLOGGINGPP(argc, argv) \
    el::Helpers::setArgs(argc, argv);   \
    std::locale::global(std::locale(""))
#else
#define START_EASYLOGGINGPP(argc, argv) el::Helpers::setArgs(argc, argv)
#endif // defined(ELPP_UNICODE)

    unicode 和非 unicode 的唯一区别在于 unicode 下还设置了语言环境。这里我们只关心命令行参数。
    从 START_EASYLOGGINGPP 宏定义可以看出,实际上解析命令行参数是通过 el::Helpers::setArgs 接口来实现的。
    el::Helpers::setArgs 接口的定义如下:

/// @copydoc setArgs(int argc, char** argv)
static inline void setArgs(int argc, const char **argv)
{
    ELPP->setApplicationArguments(argc, const_cast<char **>(argv));
}

    ELPP 宏在 偶尔日志宏 中已经介绍过了,是 easylogging++的全局管理类。
    ELPP->setApplicationArguments 的实现如下:

inline void setApplicationArguments(int argc, const char **argv)
{
    setApplicationArguments(argc, const_cast<char **>(argv));
}

    setApplicationArguments 的实现如下:

void Storage::setApplicationArguments(int argc, char **argv)
{
    // 解析命令行参数(m_commandLineArgs的类型是CommandLineArgs,作用:命令行参数解析,解析结果保存在m_commandLineArgs中,CommandLineArgs类的介绍在稍后会作详细分析)
    m_commandLineArgs.setArgs(argc, argv);
    // 从命令行参数中设置VERBOSE日志模块规则 VERBOSE日志相关的内容会在后面的文章中详细介绍。
    m_vRegistry->setFromArgs(commandLineArgs());
    // default log file
#if !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG)
    // 命令行参数中有--default-log-file这个配置项
    if (m_commandLineArgs.hasParamWithValue(base::consts::kDefaultLogFileParam))
    {
        // 全局配置默认日志文件名
        Configurations c;
        c.setGlobally(ConfigurationType::Filename,
                      std::string(m_commandLineArgs.getParamValue(base::consts::kDefaultLogFileParam)));
        registeredLoggers()->setDefaultConfigurations(c);
        // 所有日志记录器设置此基准配置
        for (base::RegisteredLoggers::iterator it = registeredLoggers()->begin();
             it != registeredLoggers()->end(); ++it)
        {
            // Logger类的configure接口前面已经介绍了,这里就不多说了
            it->second->configure(c);
        }
    }
#endif // !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG)
#if defined(ELPP_LOGGING_FLAGS_FROM_ARG)
    // 命令行参数中有--logging-flags这个配置项
    if (m_commandLineArgs.hasParamWithValue(base::consts::kLoggingFlagsParam))
    {
        int userInput = atoi(m_commandLineArgs.getParamValue(base::consts::kLoggingFlagsParam));
        if (ELPP_DEFAULT_LOGGING_FLAGS == 0x0)
        {
            // 默认logging flag没有配置,则直接设置
            m_flags = userInput;
        }
        else
        {
            // 默认logging flag有配置,则追加相关配置项(实质是按位与操作)
            base::utils::addFlag<base::type::EnumType>(userInput, &m_flags);
        }
    }
#endif // defined(ELPP_LOGGING_FLAGS_FROM_ARG)
}

通过 el:: loggers 工具类来进行配置

    el::loggers 工具类提供了一系列用于管理日志记录器和其对应配置的接口。这里我们只关心日志格式配置相关的接口。

配置某个日志记录器

配置某个日志记录器的所有配置项

/// @brief Reconfigures specified logger with new configurations
// 以Logger实例为参数来配置
Logger *Loggers::reconfigureLogger(Logger *logger, const Configurations &configurations)
{
    if (!logger)
        return nullptr;
    // Logger类的configure接口前面已经介绍了,这里就不多说了
    logger->configure(configurations);
    return logger;
}

/// @brief Reconfigures logger with new configurations after looking it up using identity
// 以Logger ID为参数来配置,内部委托给了上面的接口来实现
Logger *Loggers::reconfigureLogger(const std::string &identity, const Configurations &configurations)
{
    return Loggers::reconfigureLogger(Loggers::getLogger(identity), configurations);
}

配置某个日志记录器的某个配置项

/// @brief Reconfigures logger's single configuration
Logger *Loggers::reconfigureLogger(const std::string &identity, ConfigurationType configurationType,
                                   const std::string &value)
{
    // 获取日志记录器实例,Loggers::getLogger接口在后面的文章中会专门介绍,这里就不多说了。
    Logger *logger = Loggers::getLogger(identity);
    if (logger == nullptr)
    {
        return nullptr;
    }
    logger->configurations()->set(Level::Global, configurationType, value);
    // Logger类的configure接口前面已经介绍了,这里就不多说了
    logger->reconfigure();
    return logger;
}

    Configurations 类相关的接口在 日志格式配置管理类 中已经介绍过了。

配置所有日志记录器

配置所有日志记录器的所有配置项

/// @brief Reconfigures all the existing loggers with new configurations
void Loggers::reconfigureAllLoggers(const Configurations &configurations)
{
    for (base::RegisteredLoggers::iterator it = ELPP->registeredLoggers()->begin();
         it != ELPP->registeredLoggers()->end(); ++it)
    {
        // Loggers::reconfigureLogger接口在前面已经介绍过了
        Loggers::reconfigureLogger(it->second, configurations);
    }
}

配置所有日志记录器的某个配置项

/// @brief Reconfigures single configuration for all the loggers
static inline void reconfigureAllLoggers(ConfigurationType configurationType, const std::string &value)
{
    // Loggers::reconfigureAllLoggers接口在前面已经介绍过了
    reconfigureAllLoggers(Level::Global, configurationType, value);
}

配置所有日志记录器的某个日志级别的某个配置项

/// @brief Reconfigures single configuration for all the loggers for specified level
void Loggers::reconfigureAllLoggers(Level level, ConfigurationType configurationType, const std::string &value)
{
    for (base::RegisteredLoggers::iterator it = ELPP->registeredLoggers()->begin();
         it != ELPP->registeredLoggers()->end(); ++it)
    {
        Logger *logger = it->second;
        logger->configurations()->set(level, configurationType, value);
        // Logger类的reconfigure接口前面已经介绍了,这里就不多说了
        logger->reconfigure();
    }
}

    Configurations 类相关的接口在 日志格式配置管理类 中已经介绍过了。

配置所有日志记录器的默认配置

// 设置默认配置
void Loggers::setDefaultConfigurations(const Configurations &configurations, bool reconfigureExistingLoggers)
{
    ELPP->registeredLoggers()->setDefaultConfigurations(configurations);
    if (reconfigureExistingLoggers)
    {
        // 配置所有日志记录器的所有配置项,Loggers::reconfigureAllLoggers接口在前面已经介绍过了
        Loggers::reconfigureAllLoggers(configurations);
    }
}

    Loggers::reconfigureAllLoggers 接口在前面已经介绍过了。

// 获取默认配置对应的Configurations实例
const Configurations *Loggers::defaultConfigurations(void)
{
    return ELPP->registeredLoggers()->defaultConfigurations();
}

// 获取默认配置对应的TypedConfigurations实例
base::TypedConfigurations Loggers::defaultTypedConfigurations(void)
{
    return base::TypedConfigurations(
        ELPP->registeredLoggers()->defaultConfigurations(),
        ELPP->registeredLoggers()->logStreamsReference());
}

通过文件进行全局配置

    从全局配置文件中加载配置(仅仅支持带记录器 ID 这种形式的配置文件 如:--default

static const char* kConfigurationLoggerId = "--";

void Loggers::configureFromGlobal(const char *globalConfigurationFilePath)
{
    // 打开配置文件
    std::ifstream gcfStream(globalConfigurationFilePath, std::ifstream::in);
    ELPP_ASSERT(gcfStream.is_open(), "Unable to open global configuration file [" << globalConfigurationFilePath
                                                                                  << "] for parsing.");
    // 一行配置                                                                              
    std::string line = std::string();
    std::stringstream ss;
    Logger *logger = nullptr;
    auto configure = [&](void)
    {
        ELPP_INTERNAL_INFO(8, "Configuring logger: '" << logger->id() << "' with configurations \n"
                                                      << ss.str()
                                                      << "\n--------------");
        Configurations c;
        // Configurations类已经在上一篇文章日志格式配置管理类中已经详细介绍过了。
        c.parseFromText(ss.str());
        // Logger类的configure接口已经在前面详细介绍过了。
        logger->configure(c);
    };

    while (gcfStream.good())
    {
        // 读取一行配置
        std::getline(gcfStream, line);
        ELPP_INTERNAL_INFO(1, "Parsing line: " << line);
        // 去掉当前行头尾的空白字符
        base::utils::Str::trim(line);
        // 全是注释直接返回
        if (Configurations::Parser::isComment(line))
            continue;
        // 去掉行当中的注释
        Configurations::Parser::ignoreComments(&line);
        // 再次去掉行当中头尾的空白字符
        base::utils::Str::trim(line);
        // 去掉头尾空白字符后,全局配置文件以"--"+日志记录器ID开头,如:"-- default"
        if (line.size() > 2 && base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationLoggerId)))
        {
            //在从文件中解析新的日志记录器的配置之前,先将处理目前临时保存的日志记录器的配置
            if (!ss.str().empty() && logger != nullptr)
            {
                configure();
            }
            // 清空流的内容,以便放入新的日志记录器的内容
            ss.str(std::string(""));
            //获取当前正在解析的配置对应的日志记录器ID(跳过日志记录器ID那一行开头的"--")
            line = line.substr(2);
            //去掉日志记录器ID那一行开头的"--"后面的部分中,日志记录器ID头尾的空白字符
            base::utils::Str::trim(line);
            //日志记录器ID至少一个字符
            if (line.size() > 1)
            {
                ELPP_INTERNAL_INFO(1, "Getting logger: '" << line << "'");
                // 获取日志记录器ID对应的日志记录器,Loggers::getLogger接口后面的文章中会详细分析。
                logger = getLogger(line);
            }
        }
        else
        {
            // 不是日志记录器ID,说明还是同一个日志记录器的配置内容,直接保存
            ss << line << "\n";
        }
    }
    // 跳出循环是,还有一部分内容未处理,
    if (!ss.str().empty() && logger != nullptr)
    {
        //这部分内容是有效的日志记录器配置,则配置对应的日志记录器
        configure();
    }
}

通过命令行参数配置

bool Loggers::configureFromArg(const char *argKey)
{
#if defined(ELPP_DISABLE_CONFIGURATION_FROM_PROGRAM_ARGS)
    ELPP_UNUSED(argKey);
#else
    // Helpers::commandLineArgs():命令行参数解析器,命令行参数解析的结果保存在其中。el::Helpers工具类后面会专门介绍。
    // 则获取对应的命令行参数值(这里仅仅支持指定配置文件名而且是全局配置文件的格式,全局配置文件以"--"+日志记录器ID开头,如:"-- default")
    if (!Helpers::commandLineArgs()->hasParamWithValue(argKey))
    {
        return false;
    }
    // 获取成功后,全局配置
    configureFromGlobal(Helpers::commandLineArgs()->getParamValue(argKey));
#endif // defined(ELPP_DISABLE_CONFIGURATION_FROM_PROGRAM_ARGS)
    return true;
}

    configureFromGlobal 接口已经在前面介绍了,这里就不多说了。

    不过从官方文档里面没有看到对于这种配置方式的说明,也没有看到这个 ELPP_DISABLE_CONFIGURATION_FROM_PROGRAM_ARGS 宏的介绍。不过我们可以以类似于默认日志文件名从命令行参数加载的配置方式(--default-log-file = FILE),来配置,如:--default-global-config-file = FILE

// 例子
#include "easylogging++.h"
INITIALIZE_EASYLOGGINGPP

int main(int argc, char *argv[])
{
    // 解析命令行参数
    START_EASYLOGGINGPP(argc, argv);
    // 从指定的命令行参数如:--default-global-config-file=global.conf(global.conf为全局配置文件名称)加载配置
    el::Loggers::configureFromArg("--default-global-config-file");

    LOG(INFO) << "LOG(INFO)";
    return 0;
}

相关类介绍

CommandLineArgs 类

    CommandLineArgs 类用于解析命令行参数

成员变量

int m_argc;                                                     // 命令行参数的个数
char **m_argv;                                                  // 命令行参数数组
std::unordered_map<std::string, std::string> m_paramsWithValue; // 存放有值的命令行参数的容器
std::vector<std::string> m_params;                              // 存放无值的命令行参数的容器

成员函数

CommandLineArgs(void)
{
    setArgs(0, static_cast<char **>(nullptr));
}
CommandLineArgs(int argc, const char **argv)
{
    setArgs(argc, argv);
}
CommandLineArgs(int argc, char **argv)
{
    setArgs(argc, argv);
}

virtual ~CommandLineArgs(void) {}

/// @brief Sets arguments and parses them
inline void setArgs(int argc, const char **argv)
{
    setArgs(argc, const_cast<char **>(argv));
}

/// @brief Sets arguments and parses them
// 遍历命令行参数,将解析结果无值得存放进m_params,有值(字符串中含有'=')得存放进m_paramsWithValue
void CommandLineArgs::setArgs(int argc, char **argv)
{
    m_params.clear();
    m_paramsWithValue.clear();
    if (argc == 0 || argv == nullptr)
    {
        return;
    }
    m_argc = argc;
    m_argv = argv;
    for (int i = 1; i < m_argc; ++i)
    {
        // 查找"="首次出现的位置
        const char *v = (strstr(m_argv[i], "="));
        if (v != nullptr && strlen(v) > 0)
        {
            std::string key = std::string(m_argv[i]);
            // 获取命令行参数的名称(命令行参数中'='之前的部分)
            key = key.substr(0, key.find_first_of('='));
            // 有值命令行参数容器中是否已经有了对应的命令行参数
            if (hasParamWithValue(key.c_str()))
            {
                ELPP_INTERNAL_INFO(1, "Skipping [" << key << "] arg since it already has value ["
                                                   << getParamValue(key.c_str()) << "]");
            }
            else
            {
                // 没有则添加进有值命令行参数容器中
                m_paramsWithValue.insert(std::make_pair(key, std::string(v + 1)));
            }
        }
        // 不是有值命令行参数
        if (v == nullptr)
        {
            // 无值命令行参数容器中是否已经有了对应的命令行参数
            if (hasParam(m_argv[i]))
            {
                ELPP_INTERNAL_INFO(1, "Skipping [" << m_argv[i] << "] arg since it already exists");
            }
            else
            {
                // 没有则添加进有无值命令行参数容器中
                m_params.push_back(std::string(m_argv[i]));
            }
        }
    }
}

/// @brief Returns true if arguments contain paramKey with a value (separated by '=')
// 有值得命令行参数中是否有paramKey这个命令行参数
bool CommandLineArgs::hasParamWithValue(const char *paramKey) const
{
    return m_paramsWithValue.find(std::string(paramKey)) != m_paramsWithValue.end();
}

/// @brief Returns value of arguments
/// @see hasParamWithValue(const char*)
// 获取paramKey对应的值
const char *CommandLineArgs::getParamValue(const char *paramKey) const
{
    std::unordered_map<std::string, std::string>::const_iterator iter = m_paramsWithValue.find(std::string(paramKey));
    return iter != m_paramsWithValue.end() ? iter->second.c_str() : "";
}

/// @brief Return true if arguments has a param (not having a value) i,e without '='
// 无值的命令行参数中是否有paramKey这个命令行参数
bool CommandLineArgs::hasParam(const char *paramKey) const
{
    return std::find(m_params.begin(), m_params.end(), std::string(paramKey)) != m_params.end();
}

/// @brief Returns true if no params available. This exclude argv[0]
// 是否无命令行参数
bool CommandLineArgs::empty(void) const
{
    return m_params.empty() && m_paramsWithValue.empty();
}

/// @brief Returns total number of arguments. This exclude argv[0]
// 命令行参数总数(有值得命令行参数+无值的命令行参数)
std::size_t CommandLineArgs::size(void) const
{
    return m_params.size() + m_paramsWithValue.size();
}

base::type::ostream_t &operator<<(base::type::ostream_t &os, const CommandLineArgs &c)
{
    for (int i = 1; i < c.m_argc; ++i)
    {
        os << ELPP_LITERAL("[") << c.m_argv[i] << ELPP_LITERAL("]");
        if (i < c.m_argc - 1)
        {
            os << ELPP_LITERAL(" ");
        }
    }
    return os;
}

ConfigurationType 枚举类型

    ConfigurationType 表示日志配置项类型

/// @brief Represents enumeration of ConfigurationType used to configure or access certain aspect
/// of logging
enum class ConfigurationType : base::type::EnumType
{
    /// @brief Determines whether or not corresponding level and logger of logging is enabled
    /// You may disable all logs by using el::Level::Global
    Enabled = 1,
    /// @brief Whether or not to write corresponding log to log file
    ToFile = 2,
    /// @brief Whether or not to write corresponding level and logger log to standard output.
    /// By standard output meaning termnal, command prompt etc
    ToStandardOutput = 4,
    /// @brief Determines format of logging corresponding level and logger.
    Format = 8,
    /// @brief Determines log file (full path) to write logs to for corresponding level and logger
    Filename = 16,
    /// @brief Specifies precision of the subsecond part. It should be within range (1-6).
    SubsecondPrecision = 32,
    /// @brief Alias of SubsecondPrecision (for backward compatibility)
    MillisecondsWidth = SubsecondPrecision,
    /// @brief Determines whether or not performance tracking is enabled.
    ///
    /// @detail This does not depend on logger or level. Performance tracking always uses 'performance' logger
    PerformanceTracking = 64,
    /// @brief Specifies log file max size.
    ///
    /// @detail If file size of corresponding log file (for corresponding level) is >= specified size, log file will
    /// be truncated and re-initiated.
    MaxLogFileSize = 128,
    /// @brief Specifies number of log entries to hold until we flush pending log data
    LogFlushThreshold = 256,
    /// @brief Represents unknown configuration
    Unknown = 1010
};

Level 枚举类型

    Level 表示日志级别

/// @brief Represents enumeration for severity level used to determine level of logging
///
/// @detail With Easylogging++, developers may disable or enable any level regardless of
/// what the severity is. Or they can choose to log using hierarchical logging flag
enum class Level : base::type::EnumType
{
    /// @brief Generic level that represents all the levels. Useful when setting global configuration for all levels
    Global = 1,
    /// @brief Information that can be useful to back-trace certain events - mostly useful than debug logs.
    Trace = 2,
    /// @brief Informational events most useful for developers to debug application
    Debug = 4,
    /// @brief Severe error information that will presumably abort application
    Fatal = 8,
    /// @brief Information representing errors in application but application will keep running
    Error = 16,
    /// @brief Useful when application has potentially harmful situations
    Warning = 32,
    /// @brief Information that can be highly useful and vary with verbose logging level.
    Verbose = 64,
    /// @brief Mainly useful to represent current progress of application
    Info = 128,
    /// @brief Represents unknown level
    Unknown = 1010
};

Configuration 类

    Configuration 类是指定日志级别(el::Level)的指定配置项(el::ConfigurationType)的工具类

/// @brief Represents single configuration that has representing level, configuration type and a string based value.
///
/// @detail String based value means any value either its boolean, integer or string itself, it will be embedded inside quotes
/// and will be parsed later.
///
/// Consider some examples below:
///   * el::Configuration confEnabledInfo(el::Level::Info, el::ConfigurationType::Enabled, "true");
///   * el::Configuration confMaxLogFileSizeInfo(el::Level::Info, el::ConfigurationType::MaxLogFileSize, "2048");
///   * el::Configuration confFilenameInfo(el::Level::Info, el::ConfigurationType::Filename, "/var/log/my.log");
class Configuration : public Loggable
{
public:
    Configuration(const Configuration &c);
    Configuration &operator=(const Configuration &c);

    virtual ~Configuration(void)
    {
    }

    /// @brief Full constructor used to sets value of configuration
    Configuration(Level level, ConfigurationType configurationType, const std::string &value);

    /// @brief Gets level of current configuration
    inline Level level(void) const
    {
        return m_level;
    }

    /// @brief Gets configuration type of current configuration
    inline ConfigurationType configurationType(void) const
    {
        return m_configurationType;
    }

    /// @brief Gets string based configuration value
    inline const std::string &value(void) const
    {
        return m_value;
    }

    /// @brief Set string based configuration value
    /// @param value Value to set. Values have to be std::string; For boolean values use "true", "false", for any integral values
    ///        use them in quotes. They will be parsed when configuring
    inline void setValue(const std::string &value)
    {
        m_value = value;
    }

    virtual void log(el::base::type::ostream_t &os) const;

    /// @brief Used to find configuration from configuration (pointers) repository. Avoid using it.
    class Predicate
    {
    public:
        Predicate(Level level, ConfigurationType configurationType);

        bool operator()(const Configuration *conf) const;

    private:
        Level m_level;
        ConfigurationType m_configurationType;
    };

private:
    Level m_level;                         // 日志级别
    ConfigurationType m_configurationType; // 配置项的类型
    std::string m_value;                   // 配置项的值
};

Configuration::Configuration(const Configuration &c)
    : m_level(c.m_level), m_configurationType(c.m_configurationType), m_value(c.m_value)
{
}

Configuration &Configuration::operator=(const Configuration &c)
{
    if (&c != this)
    {
        m_level = c.m_level;
        m_configurationType = c.m_configurationType;
        m_value = c.m_value;
    }
    return *this;
}

/// @brief Full constructor used to sets value of configuration
Configuration::Configuration(Level level, ConfigurationType configurationType, const std::string &value)
    : m_level(level), m_configurationType(configurationType), m_value(value)
{
}

// 通过实现Loggable类的log接口,Configuration类可以直接进行日志输出。
void Configuration::log(el::base::type::ostream_t &os) const
{
    os << LevelHelper::convertToString(m_level)
       << ELPP_LITERAL(" ") << ConfigurationTypeHelper::convertToString(m_configurationType)
       << ELPP_LITERAL(" = ") << m_value.c_str();
}

/// @brief Used to find configuration from configuration (pointers) repository. Avoid using it.
Configuration::Predicate::Predicate(Level level, ConfigurationType configurationType)
    : m_level(level), m_configurationType(configurationType)
{
}

// 主要用于从Configurations管理类中获取指定日志级别的指定类型的配置项(Configuration)时作为谓词使用
bool Configuration::Predicate::operator()(const Configuration *conf) const
{
    return ((conf != nullptr) && (conf->level() == m_level) && (conf->configurationType() == m_configurationType));
}

至此,日志格式的配置与加载就介绍完了,下一篇我们开始介绍 VERBOSE 日志信息的管理。

posted @ 2022-12-04 15:25  节奏自由  阅读(193)  评论(0编辑  收藏  举报