本文基于glog CPP版本的0.4.0版本,对glog的实现机制做一些简单的分析记录。
概述
要记录一条日志,分为两个阶段:
- 首先生成日志。
- 然后将日志内容输出到相应的设备,如标准输出、文件等。
以下就分两部分展开对glog的分析。
日志的生成
一般有两种生成日志数据的方式:
- 类printf的方式,将需要输入的数据格式化。
- 类C++ stream流的方式,提供出来operator <
前者的好处在于可以对输入的数据格式进行严格检查,不匹配的情况下编译器会进行告警。缺点则是不够灵活。后者的好处是灵活,除了用了进行一般的日志输入,还可以写出类似
CHECK_IF(某条件不成立) << 输出日志
的操作。
glog中选择了第二种方式。
首先来看glog对外暴露的用于日志输入的接口。其对应的宏是:
#define LOG(severity) COMPACT_GOOGLE_LOG_ ## severity.stream()
#define COMPACT_GOOGLE_LOG_INFO google::LogMessage( \
__FILE__, __LINE__)
#define COMPACT_GOOGLE_LOG_WARNING google::LogMessage( \
__FILE__, __LINE__, google::GLOG_WARNING)
从中可以看到glog中每一条日志,都对应一个LogMessage的类,然后将返回其中的stream()对象输入日志数据。
每个LogMessage内部有一个名为LogMessageData的成员,用于保存这些数据,其中比较重要的成员有以下几个:
char message_text_[LogMessage::kMaxLogMessageLen+1]; // 用户存储日志的固定长度数组,大小为30KB。
LogStream stream_; // 用于接收用户日志的C++stream,构造时传入上面的message_text_来构造,所以实际写数据会到message_text_中。
void (LogMessage::*send_method_)(); // 用户最终发送日志数据的函数指针。
timestamp_、tm_time_:保存日志时间相关的成员。
实际根据日志创建出一个LogMessage对象时,会根据不同类型的日志,传入不同的send_method函数指针,而每个不同的LogMessage构造时都会去调用其内部的Init函数完成LogMessageData的构造,Init函数主要做的事情有:
- 存储send_method函数指针。
- 获取当前的系统时间,存放到相应的成员中。
而LOG之类的宏,实际返回的就是LogMessageData的stream指针,待到一切的输入完毕,这一条日志对应的L