easylogging++的那些事(四)源码分析(三)类printf接口

在上一篇我们分析了 VERBOSE日志宏。在 功能介绍 中我们介绍过使用 logger 类的 类 printf 接口来输出日志,今天来看看 logger 类的 类 printf 接口的实现。

接口创建宏

接口声明宏

    除 verbose 接口外的其他 类printf 接口(info, debug, warn, error, fatal, trace)的声明是基于 LOGGER_LEVEL_WRITERS_SIGNATURES 来完成的。
    LOGGER_LEVEL_WRITERS_SIGNATURES 宏的定义如下:

#define LOGGER_LEVEL_WRITERS_SIGNATURES(FUNCTION_NAME)                   \
    template <typename T, typename... Args>                              \
    inline void FUNCTION_NAME(const char *, const T &, const Args &...); \
    template <typename T>                                                \
    inline void FUNCTION_NAME(const T &);

接口实现宏

    除 verbose 接口外的其他接口(info, debug, warn, error, fatal, trace)的实现是基于 LOGGER_LEVEL_WRITERS 宏和 LOGGER_LEVEL_WRITERS_DISABLED 宏来完成的。
    LOGGER_LEVEL_WRITERS 宏的定义如下:

#define LOGGER_LEVEL_WRITERS(FUNCTION_NAME, LOG_LEVEL)                                    \
    template <typename T, typename... Args>                                               \
    inline void Logger::FUNCTION_NAME(const char *s, const T &value, const Args &...args) \
    {                                                                                     \
        log(LOG_LEVEL, s, value, args...);                                                \
    }                                                                                     \
    template <typename T>                                                                 \
    inline void Logger::FUNCTION_NAME(const T &value)                                     \
    {                                                                                     \
        log(LOG_LEVEL, value);                                                            \
    }

    LOGGER_LEVEL_WRITERS_DISABLED 宏的定义如下:

#define LOGGER_LEVEL_WRITERS_DISABLED(FUNCTION_NAME, LOG_LEVEL)                 \
    template <typename T, typename... Args>                                     \
    inline void Logger::FUNCTION_NAME(const char *, const T &, const Args &...) \
    {                                                                           \
        return;                                                                 \
    }                                                                           \
    template <typename T>                                                       \
    inline void Logger::FUNCTION_NAME(const T &)                                \
    {                                                                           \
        return;                                                                 \
    }

    LOGGER_LEVEL_WRITERS_DISABLED 宏从定义上来看,啥也没干,主要用于日志级别的日志输出被禁用的情况。

基于接口创建宏创建的接口

宏展开

info 接口

    info 接口的声明如下:

LOGGER_LEVEL_WRITERS_SIGNATURES(info)

    展开后为:

template <typename T, typename... Args>
inline void info(const char *, const T &, const Args &...);
template <typename T>
inline void info(const T &);

    info 接口的实现如下:

#if ELPP_INFO_LOG
LOGGER_LEVEL_WRITERS(info, Level::Info)
#else
LOGGER_LEVEL_WRITERS_DISABLED(info, Level::Info)
#endif // ELPP_INFO_LOG

    LOGGER_LEVEL_WRITERS_SIGNATURES 宏,LOGGER_LEVEL_WRITERS 宏和 LOGGER_LEVEL_WRITERS_DISABLED 宏在前面接口创建宏中已经介绍过了。
    ELPP_INFO_LOG 宏在 CLOG 宏展开 中已经详细介绍过了,用于启用 info 级别日志。
    ELPP_INFO_LOG 值为 0 时, LOGGER_LEVEL_WRITERS_DISABLED(info, Level::Info)展开后为:

template <typename T, typename... Args>
inline void Logger::info(const char *, const T &, const Args &...)
{
    return;
}

template <typename T>
inline void Logger::info(const T &)
{
    return;
}

    ELPP_INFO_LOG 值为 1 时,LOGGER_LEVEL_WRITERS(info, Level::Info) 展开后为:

template <typename T, typename... Args>
inline void Logger::info(const char *s, const T &value, const Args &...args)
{
    log(Level::Info, s, value, args...);
}

template <typename T>
inline void Logger::info(const T &value)
{
    log(Level::Info, value);
}

debug 接口

    debug 接口的声明如下:

LOGGER_LEVEL_WRITERS_SIGNATURES(debug)

    debug 接口的实现如下:

#if ELPP_DEBUG_LOG
LOGGER_LEVEL_WRITERS(debug, Level::Debug)
#else
LOGGER_LEVEL_WRITERS_DISABLED(debug, Level::Debug)
#endif // ELPP_DEBUG_LOG

    LOGGER_LEVEL_WRITERS_SIGNATURES 宏,LOGGER_LEVEL_WRITERS 宏和 LOGGER_LEVEL_WRITERS_DISABLED 宏在前面接口创建宏中已经介绍过了。
    ELPP_DEBUG_LOG 宏在 CLOG 宏展开 中已经详细介绍过了,用于启用 debug 级别日志。
    ELPP_DEBUG_LOG 值为 0 时, LOGGER_LEVEL_WRITERS_DISABLED(debug, Level::Debug) 展开后为:

template <typename T, typename... Args>
inline void Logger::debug(const char *, const T &, const Args &...)
{
    return;
}

template <typename T>
inline void Logger::debug(const T &)
{
    return;
}

    ELPP_DEBUG_LOG 值为 1 时,LOGGER_LEVEL_WRITERS(debug, Level::Debug) 展开后为:

template <typename T, typename... Args>
inline void Logger::debug(const char *s, const T &value, const Args &...args)
{
    log(Level::Debug, s, value, args...);
}

template <typename T>
inline void Logger::debug(const T &value)
{
    log(Level::Debug, value);
}

warn 接口

    warn 接口的声明如下:

LOGGER_LEVEL_WRITERS_SIGNATURES(warn)

    warn 接口的实现如下:

#if ELPP_WARNING_LOG
LOGGER_LEVEL_WRITERS(warn, Level::Warning)
#else
LOGGER_LEVEL_WRITERS_DISABLED(warn, Level::Warning)
#endif // ELPP_WARNING_LOG

    LOGGER_LEVEL_WRITERS_SIGNATURES 宏,LOGGER_LEVEL_WRITERS 宏和 LOGGER_LEVEL_WRITERS_DISABLED 宏在前面接口创建宏中已经介绍过了。
    ELPP_WARNING_LOG 宏在 CLOG 宏展开 中已经详细介绍过了,用于启用 warn 级别日志。
    ELPP_WARNING_LOG 值为 0 时, LOGGER_LEVEL_WRITERS_DISABLED(debug, Level::Debug) 展开后为:

template <typename T, typename... Args>
inline void Logger::warn(const char *, const T &, const Args &...)
{
    return;
}

template <typename T>
inline void Logger::warn(const T &)
{
    return;
}

    ELPP_WARNING_LOG 值为 1 时,LOGGER_LEVEL_WRITERS(warn, Level::Warning) 展开后为:

template <typename T, typename... Args>
inline void Logger::warn(const char *s, const T &value, const Args &...args)
{
    log(Level::Warning, s, value, args...);
}

template <typename T>
inline void Logger::warn(const T &value)
{
    log(Level::Warning, value);
}

error 接口

    error 接口的声明如下:

LOGGER_LEVEL_WRITERS_SIGNATURES(error)

    error 接口的实现如下:

#if ELPP_ERROR_LOG
LOGGER_LEVEL_WRITERS(error, Level::Error)
#else
LOGGER_LEVEL_WRITERS_DISABLED(error, Level::Error)
#endif // ELPP_ERROR_LOG

    LOGGER_LEVEL_WRITERS_SIGNATURES 宏,LOGGER_LEVEL_WRITERS 宏和 LOGGER_LEVEL_WRITERS_DISABLED 宏在前面接口创建宏中已经介绍过了。
    ELPP_ERROR_LOG 宏在 CLOG 宏展开 中已经详细介绍过了,用于启用 error 级别日志。

    ELPP_ERROR_LOG 值为 0 时, LOGGER_LEVEL_WRITERS_DISABLED(error, Level::Error) 展开后为:

template <typename T, typename... Args>
inline void Logger::error(const char *, const T &, const Args &...)
{
    return;
}

template <typename T>
inline void Logger::error(const T &)
{
    return;
}

    上面的接口其实什么也没做。

    ELPP_ERROR_LOG 值为 1 时,LOGGER_LEVEL_WRITERS(warn, Level::Error) 展开后为:

template <typename T, typename... Args>
inline void Logger::error(const char *s, const T &value, const Args &...args)
{
    log(Level::Error, s, value, args...);
}

template <typename T>
inline void Logger::error(const T &value)
{
    log(Level::Error, value);
}

fatal 接口

    fatal 接口的声明如下:

LOGGER_LEVEL_WRITERS_SIGNATURES(fatal)

    fatal 接口的实现如下:

#if ELPP_FATAL_LOG
LOGGER_LEVEL_WRITERS(fatal, Level::Fatal)
#else
LOGGER_LEVEL_WRITERS_DISABLED(fatal, Level::Fatal)
#endif // ELPP_FATAL_LOG

    LOGGER_LEVEL_WRITERS_SIGNATURES 宏,LOGGER_LEVEL_WRITERS 宏和 LOGGER_LEVEL_WRITERS_DISABLED 宏在前面接口创建宏中已经介绍过了。
    ELPP_FATAL_LOG 宏在 CLOG 宏展开 中已经详细介绍过了,用于启用 fatal 级别日志。
    ELPP_FATAL_LOG 值为 0 时, LOGGER_LEVEL_WRITERS_DISABLED(fatal, Level::Fatal) 展开后为:

template <typename T, typename... Args>
inline void Logger::fatal(const char *, const T &, const Args &...)
{
    return;
}

template <typename T>
inline void Logger::fatal(const T &)
{
    return;
}

    ELPP_FATAL_LOG 值为 1 时,LOGGER_LEVEL_WRITERS(fatal, Level::Fatal) 展开后为:

template <typename T, typename... Args>
inline void Logger::fatal(const char *s, const T &value, const Args &...args)
{
    log(Level::Fatal, s, value, args...);
}

template <typename T>
inline void Logger::fatal(const T &value)
{
    log(Level::Fatal, value);
}

trace 接口

    trace 接口的声明如下:

LOGGER_LEVEL_WRITERS_SIGNATURES(trace)

    trace 接口的实现如下:

#if ELPP_TRACE_LOG
LOGGER_LEVEL_WRITERS(trace, Level::Trace)
#else
LOGGER_LEVEL_WRITERS_DISABLED(trace, Level::Trace)
#endif // ELPP_TRACE_LOG

    LOGGER_LEVEL_WRITERS_SIGNATURES 宏,LOGGER_LEVEL_WRITERS 宏和 LOGGER_LEVEL_WRITERS_DISABLED 宏在前面接口创建宏中已经介绍过了。
    ELPP_TRACE_LOG 宏在 CLOG 宏展开 中中已经详细介绍过了,用于启用 trace 级别日志。
    ELPP_TRACE_LOG 值为 0 时, LOGGER_LEVEL_WRITERS_DISABLED(trace, Level::Trace) 展开后为:

template <typename T, typename... Args>
inline void Logger::trace(const char *, const T &, const Args &...)
{
    return;
}

template <typename T>
inline void Logger::trace(const T &)
{
    return;
}

    ELPP_TRACE_LOG 值为 1 时,LOGGER_LEVEL_WRITERS(trace, Level::Trace) 展开后为:

template <typename T, typename... Args>
inline void Logger::trace(const char *s, const T &value, const Args &...args)
{
    log(Level::Trace, s, value, args...);
}

template <typename T>
inline void Logger::trace(const T &value)
{
    log(Level::Trace, value);
}

源码分析

    上面 类printf 接口(info, debug, warn, error, fatal, trace)实际上都委托给了对应的 log 可变参函数模板来实现:

template <typename T, typename... Args>
inline void Logger::log(Level level, const char *s, const T &value, const Args &...args)
{
    acquireLock(); // released in Writer!
    log_(level, 0, s, value, args...);
}

//log可变参函数模板的退出接口
template <typename T>
inline void Logger::log(Level level, const T &log)
{
    acquireLock(); // released in Writer!
    log_(level, 0, log);
}

    这两个 log 接口只不过是对应 log_ 接口的增强类, 增强的地方在于加锁了 。实际功能再次委托给了对应 log_可变参函数模板:

template <typename T, typename... Args>
void Logger::log_(Level level, int vlevel, const char *s, const T &value, const Args &...args)
{
    // MessageBuilder 类在 CLOG 宏其他相关类中中已经详细介绍过了, 用于支持各种类型的日志输出
    base::MessageBuilder b;
    b.initialize(this);
    while (*s)
    {
        // 当前字符是%
        if (*s == base::consts::kFormatSpecifierChar)
        {
            if (*(s + 1) == base::consts::kFormatSpecifierChar)
            {
                // 下一个字符也是%,则跳过当前字符,并直接将%字符放入日志记录器的字符串流对象中
                ++s;
            }
            else
            {
                // 下一个字符也是 v
                if (*(s + 1) == base::consts::kFormatSpecifierCharValue)
                {
                    ++s;
                    // 将当前的可变参放入日志记录器的字符串流对象中(类 printf 接口中,%v 这种形式的格式说明符可以支持所有的数据类型(需要 MessageBuilder 类提供了针对这种类型的日志输出支持))
                    b << value;
                    // 跳过 "%v" 格式说明符, 继续递归解析剩余的格式化字符串
                    log_(level, vlevel, ++s, args...);
                    return;
                }
            }
        }
        // 非“%v”, 非“%%”的其他形式的字符,直接放入日志记录器的字符串流对象中
        b << *s++;
    }
    ELPP_INTERNAL_ERROR("Too many arguments provided. Unable to handle. Please provide more format specifiers", false);
}

    上面的实现本身并不复杂,无非是对 s(相当于 printf 中的格式化字符串)逐个字符进行解析,调整后,放入日志记录器的字符串流对象中。
    从上面的实现中可以看到: logger 类的类 printf 接口只能解析 “%v”“%%” 这种格式说明符,其他配置文件中支持的日志格式指示器,这里都不支持。

    下面这个接口是上面 log_ 可变参函数模板的退出接口。可变参函数模板一般都会定义这样的两个接口。

template <typename T>
void Logger::log_(Level level, int vlevel, const T &log)
{
    // 是否 VERBOSE 日志
    if (level == Level::Verbose)
    {
        // 当前文件在当前 VLEVEL 是否允许进行 VERBOSE 日志输出
        if (ELPP->vRegistry()->allowed(vlevel, __FILE__))
        {
            // 这里是通过 verbose 接口进到这里来的,verbose 接口入口处已经加锁了,所以这里 Writer 类里面的接口不用再次加锁了,所以传递 false
            base::Writer(Level::Verbose, "FILE", 0, "FUNCTION",
                         base::DispatchAction::NormalLog, vlevel)
                    .construct(this, false)
                << log;
        }
        else
        {
            // 不允许进行 VERBOSE 日志输出则清空日志记录器对应的字符串流对象
            stream().str(ELPP_LITERAL(""));
            // verbose 接口入口处已经加锁了,这里需要释放锁
            releaseLock();
        }
    }
    else
    {
        // 分层日志
        base::Writer(level, "FILE", 0, "FUNCTION").construct(this, false) << log;
    }
}

    el::base::Writer 类我们在 CLOGWriter 对象的创建以及初始化日志输出日志信息保存 已经仔细介绍过了,这里就不多说了。

verbose 接口

    verbose 接口的声明如下:

template <typename T, typename... Args>
inline void verbose(int, const char *, const T &, const Args &...);

template <typename T>
inline void verbose(int, const T &);

    verbose 接口的实现如下:

#if ELPP_VERBOSE_LOG
template <typename T, typename... Args>
inline void Logger::verbose(int vlevel, const char *s, const T &value, const Args &...args)
{
    acquireLock(); // released in Writer!
    log_(el::Level::Verbose, vlevel, s, value, args...);
}
template <typename T>
inline void Logger::verbose(int vlevel, const T &log)
{
    acquireLock(); // released in Writer!
    log_(el::Level::Verbose, vlevel, log);
}
#else
template <typename T, typename... Args>
inline void Logger::verbose(int, const char *, const T &, const Args &...)
{
    return;
}
template <typename T>
inline void Logger::verbose(int, const T &)
{
    return;
}
#endif // ELPP_VERBOSE_LOG

    ELPP_VERBOSE_LOG 宏在 VERBOSE日志宏 中已经介绍过了,用于启用 VERBOSE 日志。
    ELPP_VERBOSE_LOG 值为 0 时,两个 verbose 接口什么也没做。
    ELPP_VERBOSE_LOG 值为 1 时,两个 verbose 接口实际是委托给对应的 log_ 接口来实现的。
    log_ 接口在前面已经详细介绍过了,这里就不多说了。

至此,logger 类的类 printf 接口就介绍完了,下一篇我们开始介绍日志格式的配置与加载。

posted @ 2022-12-01 16:39  节奏自由  阅读(77)  评论(0编辑  收藏  举报