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::Writer的construct接口返回的是对象本身即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::MessageBuilder的m_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_logger的stream流其实就是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 宏接口调用流程图

CLOG 宏的日志信息保存部分到这里就介绍完了,下一篇我们分析一下前面 CLOG 宏的流程中使用到的其他相关类。
本文来自博客园,作者:节奏自由,转载请注明原文链接:https://www.cnblogs.com/DesignLife/p/16927938.html

浙公网安备 33010602011771号