easylogging++的那些事(四)源码分析(二)日志记录宏(一)CLOG宏(一)宏展开

在 上一篇中我们分析了 easylogging++的 主流程,今天来看看日志记录宏中 CLOG 宏的实现。
在 easylogging++的 功能介绍 中我们详细介绍了日志记录宏的一些用法,现在我们来一一剖析这些宏的实现。
先看看 CLOG 宏,CLOG 宏定义如下:

  #define CLOG(LEVEL, ...)\
    C##LEVEL(el::base::Writer, el::base::DispatchAction::NormalLog, __VA_ARGS__)

其中 ## 是连字符,__VA_ARGS__ 原样替换 ...
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
    };

Info 日志宏 CLOG(INFO, xxx)

    用个具体的例子就一目了然了:

    CLOG(INFO, "default");

    上面实际展开后为:

   CINFO(el::base::Writer, el::base::DispatchAction::NormalLog, "default");

    而 CINFO 是另外一个宏:

    #if ELPP_INFO_LOG
    #define CINFO(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Info, dispatchAction, __VA_ARGS__)
    #else
    #define CINFO(writer, dispatchAction, ...) el::base::NullWriter()
    #endif // ELPP_INFO_LOG

    ELPP_INFO_LOG 的定义如下:

    #if (!defined(ELPP_DISABLE_INFO_LOGS) && (ELPP_LOGGING_ENABLED))
    #define ELPP_INFO_LOG 1
    #else
    #define ELPP_INFO_LOG 0
    #endif // (!defined(ELPP_DISABLE_INFO_LOGS) && (ELPP_LOGGING_ENABLED))

    ELPP_DISABLE_INFO_LOGSELPP_LOGGING_ENABLED 用于启用 info 日志级别。

    ELPP_INFO_LOG 值为 0 时,直接就是 el:: base:: NullWriter(),创建了 el:: base:: NullWriter 对象,el:: base:: NullWriter 后面会解释,这里简单提一下,就是用于: 当日志被禁用的使用,表示不进行输出。
    ELPP_INFO_LOG 值为 1 时,上面的 CINFO(el::base::Writer, el::base::DispatchAction::NormalLog, "default"); 展开后为:

    ELPP_WRITE_LOG(el::base::Writer, el::Level::Info, el::base::DispatchAction::NormalLog, "default");

    而 ELPP_WRITE_LOG 也是一个宏:

    #define ELPP_WRITE_LOG(writer, level, dispatchAction, ...) \
    writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__)

    进一步展开后:

    el::base::Writer(el::Level::Info, __FILE__, __LINE__, ELPP_FUNC, el::base::DispatchAction::NormalLog).construct(el_getVALength("default"), "default");

    其中 el_getVALength 也是一个宏:

    #if ELPP_COMPILER_MSVC
    #define ELPP_VARIADIC_FUNC_MSVC(variadicFunction, variadicArgs) variadicFunction variadicArgs
    #define ELPP_VARIADIC_FUNC_MSVC_RUN(variadicFunction, ...) ELPP_VARIADIC_FUNC_MSVC(variadicFunction, (__VA_ARGS__))
    #define el_getVALength(...) ELPP_VARIADIC_FUNC_MSVC_RUN(el_resolveVALength, 0, ##__VA_ARGS__, \
                                                            10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
    #else
    #if ELPP_COMPILER_CLANG
    #define el_getVALength(...) el_resolveVALength(0, __VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
    #else
    #define el_getVALength(...) el_resolveVALength(0, ##__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
    #endif // ELPP_COMPILER_CLANG
    #endif // ELPP_COMPILER_MSVC

    因为可变参宏在不同平台的差异性,windows(ELPP_COMPILER_MSVC 宏)下需要经过多次中间替换。最终经过替换后看起来就是下面这样:

    el_resolveVALength(0, "default", 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0);

    el_resolveVALength 也是一个宏:

    #define el_resolveVALength(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N

    在easylogging++的 宏定义 中介绍过 el_resolveVALength 宏,是用于获取可变参的数量( 目前源码当中最多只能获取的可变参数量限制在 10 个 )。
    这里通过可变参数量的变化,让这些占位参数分别对应不同的值,从而间接让 N 正好对应的值是可变参的数量 ,大家这里可以试试分别获取可变参数量在 0-10 之间的可变参的实际数量,比如这里的 N 正好对应 1,也就是可变参数量为 1。

    将 el_getVALength("default") 换成这个 N 的值 1,最终展开为:

    el::base::Writer(el::Level::Info, __FILE__, __LINE__, ELPP_FUNC, el::base::DispatchAction::NormalLog).construct(1, "default");

    到这里为止,所有的宏替换就完成了,实际相当于创建了 el:: base:: Writer 类的实例,还是个临时对象。

    其他日志级别宏的展开类似。

Trace 日志宏 CLOG(TRACE, XXX)

    用个具体的例子就一目了然了:

    CLOG(TRACE, "default");

    上面实际展开后为:

    CTRACE(el::base::Writer, el::base::DispatchAction::NormalLog, "default");

    而 CTRACE 是另外一个宏:

    #if ELPP_TRACE_LOG
    #define CTRACE(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Trace, dispatchAction, __VA_ARGS__)
    #else
    #define CTRACE(writer, dispatchAction, ...) el::base::NullWriter()
    #endif // ELPP_TRACE_LOG

    ELPP_TRACE_LOG 宏定义如下:

    #if (!defined(ELPP_DISABLE_TRACE_LOGS) && (ELPP_LOGGING_ENABLED))
    #define ELPP_TRACE_LOG 1
    #else
    #define ELPP_TRACE_LOG 0
    #endif // (!defined(ELPP_DISABLE_TRACE_LOGS) && (ELPP_LOGGING_ENABLED))

    ELPP_DISABLE_TRACE_LOGSELPP_LOGGING_ENABLED 用于启用 Trace 级别日志。

    ELPP_TRACE_LOG 宏值为 0 时,直接就是 el:: base:: NullWriter() 这个前面 Info 日志宏分析时,已经简单介绍过了。
    ELPP_TRACE_LOG 宏值为 1 时,上面的 CTRACE(el:: base:: Writer, el:: base:: DispatchAction:: NormalLog, "default") 展开后为:

    ELPP_WRITE_LOG(el::base::Writer, el::Level::Trace, el::base::DispatchAction::NormalLog, "default");

    而 ELPP_WRITE_LOG 宏前面介绍过了,这里直接展开:

    el::base::Writer(el::Level::Trace, __FILE__, __LINE__, ELPP_FUNC, el::base::DispatchAction::NormalLog).construct(el_getVALength("default"), "default");

    el_getVALength 宏前面 Info 级别日志宏已经介绍过了,表示可变参的数目,这里 el_getVALength("default") 值为 1,再次替换后:

    el::base::Writer(el::Level::Trace, __FILE__, __LINE__, ELPP_FUNC, el::base::DispatchAction::NormalLog).construct(1, "default");

    到这里为止,所有的宏替换就完成了,和 Info 级别日志宏一样,实际也是相当于创建了 el:: base:: Writer 类的实例,还是个临时对象。

Debug 日志宏 CLOG(DEBUG, XXX)

    用个具体的例子就一目了然了:

    CLOG(DEBUG, "default");

    上面实际展开后为:

    CDEBUG(el::base::Writer, el::base::DispatchAction::NormalLog, "default");

    而 CDEBUG 是另外一个宏:

    #if ELPP_DEBUG_LOG
    #define CDEBUG(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Debug, dispatchAction, __VA_ARGS__)
    #else
    #define CDEBUG(writer, dispatchAction, ...) el::base::NullWriter()
    #endif // ELPP_DEBUG_LOG

    ELPP_DEBUG_LOG 宏定义如下:

    #if (!defined(ELPP_DISABLE_DEBUG_LOGS) && (ELPP_LOGGING_ENABLED))
    #define ELPP_DEBUG_LOG 1
    #else
    #define ELPP_DEBUG_LOG 0
    #endif // (!defined(ELPP_DISABLE_DEBUG_LOGS) && (ELPP_LOGGING_ENABLED))

    ELPP_DISABLE_DEBUG_LOGS 宏和 ELPP_LOGGING_ENABLED 宏用于开启 Debug 级别日志。

    当 ELPP_DEBUG_LOG 宏值为 0 时,直接就是 el:: base:: NullWriter() 这个前面 Info 日志宏分析时,已经简单介绍过了。
    当 ELPP_DEBUG_LOG 宏值为 1 时,上面的 CDEBUG(el:: base:: Writer, el:: base:: DispatchAction:: NormalLog, "default") 展开后为:

    ELPP_WRITE_LOG(el::base::Writer, el::Level::Debug, el::base::DispatchAction::NormalLog, "default");

    而 ELPP_WRITE_LOG 宏前面介绍过了,这里直接展开:

    el::base::Writer(el::Level::Debug, __FILE__, __LINE__, ELPP_FUNC, el::base::DispatchAction::NormalLog).construct(el_getVALength("default"), "default");

    el_getVALength 宏前面 Info 级别日志宏已经介绍过了,表示可变参的数目,这里 el_getVALength("default") 值为 1,再次替换后:

    el::base::Writer(el::Level::Debug, __FILE__, __LINE__, ELPP_FUNC, el::base::DispatchAction::NormalLog).construct(1, "default");

    到这里为止,所有的宏替换就完成了,和 Info 级别日志宏一样,实际相当于创建了 el:: base:: Writer 类的实例,还是个临时对象。

Fatal 日志宏 CLOG(FATAL, XXX)

    用个具体的例子就一目了然了:

    CLOG(FATAL, "default");

    上面实际展开后为:

    CFATAL(el::base::Writer, el::base::DispatchAction::NormalLog, "default");

    而 CFATAL 是另外一个宏:

    #if ELPP_FATAL_LOG
    #define CFATAL(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Fatal, dispatchAction, __VA_ARGS__)
    #else
    #define CFATAL(writer, dispatchAction, ...) el::base::NullWriter()
    #endif  // ELPP_FATAL_LOG

    ELPP_FATAL_LOG 的定义如下:

    #if (!defined(ELPP_DISABLE_FATAL_LOGS) && (ELPP_LOGGING_ENABLED))
    #define ELPP_FATAL_LOG 1
    #else
    #define ELPP_FATAL_LOG 0
    #endif  // (!defined(ELPP_DISABLE_FATAL_LOGS) && (ELPP_LOGGING_ENABLED))

    ELPP_DISABLE_FATAL_LOGS 宏和 ELPP_LOGGING_ENABLED 宏用于启用 Fatal 级别日志。

    当 ELPP_FATAL_LOG 宏值为 0 时,直接就是 el:: base:: NullWriter() 这个前面 Info 日志宏分析时,已经简单介绍过了。
    当 ELPP_FATAL_LOG 宏值为 1 时,上面的 CFATAL(el:: base:: Writer, el:: base:: DispatchAction:: NormalLog, "default") 展开后为:

    ELPP_WRITE_LOG(el::base::Writer, el::Level::Debug, el::base::DispatchAction::NormalLog, "default");

    而 ELPP_WRITE_LOG 宏前面介绍过了,这里直接展开:

    el::base::Writer(el::Level::Fatal, __FILE__, __LINE__, ELPP_FUNC, el::base::DispatchAction::NormalLog).construct(el_getVALength("default"), "default");

    el_getVALength 宏前面 Info 级别日志宏已经介绍过了,表示可变参的数目,这里 el_getVALength("default") 值为 1,再次替换后:

    el::base::Writer(el::Level::Fatal, __FILE__, __LINE__, ELPP_FUNC, el::base::DispatchAction::NormalLog).construct(1, "default");

    到这里为止,所有的宏替换就完成了,和 Info 级别日志宏一样,实际相当于创建了 el:: base:: Writer 类的实例,还是个临时对象。

Error 日志宏 CLOG(ERROR, XXX)

    用个具体的例子就一目了然了:

    CLOG(ERROR, "default");

    上面实际展开后为:

    CERROR(el::base::Writer, el::base::DispatchAction::NormalLog, "default");

    而 CERROR 是另外一个宏:

    #if ELPP_ERROR_LOG
    #define CERROR(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Error, dispatchAction, __VA_ARGS__)
    #else
    #define CERROR(writer, dispatchAction, ...) el::base::NullWriter()
    #endif  // ELPP_ERROR_LOG

    ELPP_ERROR_LOG 的定义如下:

    #if (!defined(ELPP_DISABLE_ERROR_LOGS) && (ELPP_LOGGING_ENABLED))
    #define ELPP_ERROR_LOG 1
    #else
    #define ELPP_ERROR_LOG 0
    #endif  // (!defined(ELPP_DISABLE_ERROR_LOGS) && (ELPP_LOGGING_ENABLED))

    ELPP_DISABLE_ERROR_LOGS 宏和 ELPP_LOGGING_ENABLED 宏用于开启 Error 级别日志。

    当 ELPP_ERROR_LOG 宏值为 0 时,直接就是 el:: base:: NullWriter(), 这个前面 Info 日志宏分析时,已经简单介绍过了。
    当 ELPP_ERROR_LOG 宏值为 1 时,上面的 CERROR(el:: base:: Writer, el:: base:: DispatchAction:: NormalLog, "default") 展开后为:

    ELPP_WRITE_LOG(el::base::Writer, el::Level::Error, el::base::DispatchAction::NormalLog, "default");

    而 ELPP_WRITE_LOG 宏前面介绍过了,这里直接展开:

    el::base::Writer(el::Level::Error, __FILE__, __LINE__, ELPP_FUNC, el::base::DispatchAction::NormalLog).construct>> (el_getVALength("default"), "default");

    el_getVALength 宏前面 Info 级别日志宏已经介绍过了,表示可变参的数目,这里 el_getVALength("default") 值为 1,再次替换后:

    el::base::Writer(el::Level::Error, __FILE__, __LINE__, ELPP_FUNC, el::base::DispatchAction::NormalLog).construct(1, "default");

    到这里为止,所有的宏替换就完成了,和 Info 级别日志宏一样,实际相当于创建了 el:: base:: Writer 类的实例,还是个临时对象。

Warning 日志宏 CLOG(WARNING, XXX)

    用个具体的例子就一目了然了:

    CLOG(WARNING, "default");

    上面实际展开后为:

    CWARNING(el::base::Writer, el::base::DispatchAction::NormalLog, "default");

    而 CWARNING 是另外一个宏:

    #if ELPP_WARNING_LOG
    #define CWARNING(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Warning, dispatchAction, __VA_ARGS__)
    #else
    #define CWARNING(writer, dispatchAction, ...) el::base::NullWriter()
    #endif  // ELPP_WARNING_LOG

    ELPP_WARNING_LOG 宏定义如下:

    #if (!defined(ELPP_DISABLE_WARNING_LOGS) && (ELPP_LOGGING_ENABLED))
    #define ELPP_WARNING_LOG 1
    #else
    #define ELPP_WARNING_LOG 0
    #endif  // (!defined(ELPP_DISABLE_WARNING_LOGS) && (ELPP_LOGGING_ENABLED))

    ELPP_DISABLE_WARNING_LOGS 宏和 ELPP_LOGGING_ENABLED 宏用于开启 Error 级别日志。

    当 ELPP_WARNING_LOG 宏值为 0 时,直接就是 el:: base:: NullWriter() 这个前面 Info 日志宏分析时,已经简单介绍过了。
    当 ELPP_WARNING_LOG 宏值为 1 时,上面的 CWARNING(el:: base:: Writer, el:: base:: DispatchAction:: NormalLog, "default") 展开后为:

    ELPP_WRITE_LOG(el::base::Writer, el::Level::Warning, el::base::DispatchAction::NormalLog, "default");

    而 ELPP_WRITE_LOG 宏前面介绍过了,这里直接展开:

    el::base::Writer(el::Level::Warning, __FILE__, __LINE__, ELPP_FUNC, el::base::DispatchAction::NormalLog).construct>> (el_getVALength("default"), "default");

    el_getVALength 宏前面 Info 级别日志宏已经介绍过了,表示可变参的数目,这里 el_getVALength("default") 值为 1,再次替换后:

    el::base::Writer(el::Level::Warning, __FILE__, __LINE__, ELPP_FUNC, el::base::DispatchAction::NormalLog).construct(1, "default");

    到这里为止,所有的宏替换就完成了,和 Info 级别日志宏一样,实际相当于创建了 el:: base:: Writer 类的实例,还是个临时对象。

CLOG 宏的展开到这里就介绍完了,下一篇文章我们开始分析 el:: base:: Writer 类实例的创建过程。

posted @ 2022-11-25 21:24  节奏自由  阅读(132)  评论(0编辑  收藏  举报