easylogging++的那些事(四)源码分析(二)日志记录宏(一)CLOG宏(四)日志信息保存

在上一篇中我们分析完了 CLOG日志输出 的流程,在结尾的时候我们提出了一个问题:

    CLOG(INFO, "default") << "This is a CLOG!";

CLOG 宏的流式输出是如何实现的?今天我们就来解答这个问题。

writer 类的输出运算符

    前面我们经过分析知道 CLOG(INFO, "default") 经过展开后相当于:

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

    而 el:: base::Writerconstruct 接口返回的是对象本身即 writer 实例,也就是说这里相当于调用的是 writer 类的输出运算符,接下来我们看看 writer 类的输出运算符都做了什么:

    template <typename T>
    inline Writer &operator<<(const T &log)
    {
    #if ELPP_LOGGING_ENABLED
        if (m_proceed)
        {
            m_messageBuilder << log;
        }
    #endif // ELPP_LOGGING_ENABLED
        return *this;
    }
    
    inline Writer &operator<<(std::ostream &(*log)(std::ostream &))
    {
    #if ELPP_LOGGING_ENABLED
        if (m_proceed)
        {
            m_messageBuilder << log;
        }
    #endif // ELPP_LOGGING_ENABLED
        return *this;
    }

writer 类的流操控符

    writer 类重载的输出运算符有两个,一个是模板,一个是参数为 std:: ostream& (*log)(std:: ostream&) 类型的函数指针。
    对于 C++流操控符 有所了解的应该知道,我们平时使用 std::cout << std::endl; 其中的 std::endl 其实就是流操控符,它的类型实际上是一个函数模板的实例,函数模板类型为:

    template <class _Elem, class _Traits>
    std::basic_ostream<_Elem, _Traits> &std::endl(std::basic_ostream<_Elem, _Traits> &_Ostr);

    而 cout 的类型为 std::ostream,所以上面的模板经过实例化为:

std::ostream& std::endl<char, char_traits<char>>(std::ostream& _Ostr);

    所以上面的第二个参数类型为 std::ostream& (*log)(std::ostream&) 的输出运算符相当于让 writer 拥有了使用流操控符的能力。也就是说我们可以这样使用 writer

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

    当然我们也可以定制自己的流操控符,这已经相当于输入输出流方面的内容了,与日志库关系不大,限于篇幅这里就不多说了。

    再回到上面这两个 writer 的重载的输出运算符上面来,其实现是一样的:

    #if ELPP_LOGGING_ENABLED
    // 启用输出日志
    if (m_proceed)
    {
        // 需要处理这条日志
        m_messageBuilder << log;
    }
    #endif // ELPP_LOGGING_ENABLED
    return *this;

    这里我们看到 writer 类的输出运算符实际上是委托给 m_messageBuilder 的输出运算符来实现的。

el:: base:: MessageBuilder 类

    而 m_messageBuilder 的类型为 el:: base::MessageBuilder,前面我们简单的提过,el:: base::MessageBuilder 用于支持各种类型的日志输出。

    el:: base::MessageBuilder 重载了各种类型的输出运算符,同时也支持流操控符(如 std::endl)。如:内置类型STL 相关类型、QT 相关类型、boost 相关类型、WXWIDGETS 相关类型以及 其他类型 的支持。

    el:: base::MessageBuilder 所有上述输出运算符实际上做的事情内部都是委托给了 m_logger 成员对应的 stream 流。通过 m_logger 对应的 stream 流最终将日志信息暂时保存起来。
    el:: base::MessageBuilderm_logger 成员是前面 writer 实例调用 constrcut 接口初始化 m_messageBuilder 时( m_messageBuilder.initialize(m_logger); )作为参数传递进去的来,

    el:: base:: MessageBuilder的输出运算符,我们随便看个基本类型的实现就一清二楚了:

    #define ELPP_SIMPLE_LOG(LOG_TYPE)                    \
        MessageBuilder &operator<<(LOG_TYPE msg)         \
        {                                                \
            m_logger->stream() << msg;                   \
            if (ELPP->hasFlag(LoggingFlag::AutoSpacing)) \
            {                                            \
                m_logger->stream() << " ";               \
            }                                            \
            return *this;                                \
        }
    
    ELPP_SIMPLE_LOG(char)

    上面的这个宏 ELPP_SIMPLE_LOG(char) 扩展开为:

    MessageBuilder &operator<<(char msg)
    {
        m_logger->stream() << msg;
        if (ELPP->hasFlag(LoggingFlag::AutoSpacing))
        {
            m_logger->stream() << " ";
        }
        return *this;
    }

    关于 MessageBuilder 的更多的内容后面会作更详细的分析。

    而 m_loggerstream 流其实就是 base:: type::stringstream_t 其实就是标准库的字符串流.
    base:: type:: stringstream_t 实际是个类型别名,定义如下:
    下面的宏我省略了多余的部分,只看关键的部分。

    #if defined(ELPP_UNICODE)
    typedef std::wstringstream stringstream_t;
    #else
    typedef std::stringstream stringstream_t;
    #endif // defined(ELPP_UNICODE)

上面 CLOG 宏的所有这些分析用一句话总结就是:

writer 对象重载的输出运算符可以将要输出的信息保存到当前写日志对应的日志记录器的字符串流对象中,而 writer 对象 离开作用域时调用的析构函数最终实现了将日志记录器的字符串流对象中保存的这些信息输出到文件或者终端或者其他输出目的地。

CLOG 宏接口调用流程图

image

CLOG 宏的日志信息保存部分到这里就介绍完了,下一篇我们分析一下前面 CLOG 宏的流程中使用到的其他相关类。

posted @ 2022-11-26 18:07  节奏自由  阅读(145)  评论(0编辑  收藏  举报