整体的思路为:Logging类定义日志的等级,在Logging类中包含一个Impl类用来执行,Impl类中还包含一个Logstream类,类似于iostream这种重载<< >>操作将数据输入到缓冲区。
Logging->Impl->LogStream->FixedBuffer.
目录
1.Logger类
Logger包含一个enum表示日志的等级
enum LogLevel
{
TRACE,
DEBUG,
INFO,
WARN,
ERROR,
FATAL,
NUM_LOG_LEVELS,
};
还包含一个sourfile类主要是用来包装__FILE__的,保留basename。比如对于一个路径lhb/home/下载/test.cpp 其中test.cpp就是basename。
最后是一个Impl类,Impl类中有一个LogStream对象,最终的操作都会转到Impl类中去执行,并将内容添加到LogStream对象的缓冲区中。在Logger的构造函数中最终都是调用impl的构造函数。这里有一个小细节就是最后一个构造函数,当是关闭程序的时候日志等级为FATAL,否则为ERROR。
Logger::Logger(SourceFile file, int line)
: impl_(INFO, 0, file, line)
{
}
Logger::Logger(SourceFile file, int line, LogLevel level, const char* func)
: impl_(level, 0, file, line)
{
impl_.stream_ << func << ' ';
}
Logger::Logger(SourceFile file, int line, LogLevel level)
: impl_(level, 0, file, line)
{
}
Logger::Logger(SourceFile file, int line, bool toAbort)
: impl_(toAbort?FATAL:ERROR, errno, file, line)
{
}
而impl构造函数会将这些信息缓存到LogStream对象的缓冲区中,其中的T帮助在编译的时候知道字符串的长度。
Logger::Impl::Impl(LogLevel level, int savedErrno, const SourceFile& file, int line)
: time_(Timestamp::now()),
stream_(),
level_(level),
line_(line),
basename_(file)
{
formatTime();
CurrentThread::tid();
stream_ << T(CurrentThread::tidString(), CurrentThread::tidStringLength());//打印当前线程的tid
stream_ << T(LogLevelName[level], 6);打印//日志等级
if (savedErrno != 0)
{
stream_ << strerror_tl(savedErrno) << " (errno=" << savedErrno << ") ";
}//如果有错误信息的话打印出错误信息。
}
还有一些功能比如就是设置输出的地方的。
typedef void (*OutputFunc)(const char* msg, int len);
typedef void (*FlushFunc)();
static void setOutput(OutputFunc);
static void setFlush(FlushFunc);
最后是析构函数,析构函数先调用finish函数,而finish函数是将一些最后的信息打印到缓冲区中,然后返回LogStream中的缓冲区并且将其打印到默认的输出地方,最后判断是否关闭程序。
2.LogStream
LogStream包含一个FixedBuffer,然后重载<<操作符,所有的操作都是将内容添加到FixedBuffer中。其中最为精彩的地方是将整数转换为字符串,我在其中添加了注释
const char digits[] = "9876543210123456789";
const char* zero = digits + 9;
// Efficient Integer to String Conversions, by Matthew Wilson.
template<typename T>
size_t convert(char buf[], T value)
{
T i = value;
char* p = buf;
do
{
int lsd = static_cast<int>(i % 10);
i /= 10;
*p++ = zero[lsd];//这里是考虑还有负数的情况,假如lsd为负数那么根据zero的索引最后得到的字符也是正确的
} while (i != 0);
if (value < 0)
{
*p++ = '-';
}
*p = '\0';
std::reverse(buf, p);//最后将其反正
return p - buf;//然后返回添加的长度
}
而将浮点数转换为字符串时作者用了snprintf。 其实很多操作我都进行了简化,主要是现在的我因为缺乏经验而不明白其中的用意。
三用法
首先作者定义了如下的宏
#define LOG_TRACE if (muduo::Logger::logLevel() <= muduo::Logger::TRACE) \
muduo::Logger(__FILE__, __LINE__, muduo::Logger::TRACE, __func__).stream()
#define LOG_DEBUG if (muduo::Logger::logLevel() <= muduo::Logger::DEBUG) \
muduo::Logger(__FILE__, __LINE__, muduo::Logger::DEBUG, __func__).stream()
#define LOG_INFO if (muduo::Logger::logLevel() <= muduo::Logger::INFO) \
muduo::Logger(__FILE__, __LINE__).stream()
#define LOG_WARN muduo::Logger(__FILE__, __LINE__, muduo::Logger::WARN).stream()
#define LOG_ERROR muduo::Logger(__FILE__, __LINE__, muduo::Logger::ERROR).stream()
#define LOG_FATAL muduo::Logger(__FILE__, __LINE__, muduo::Logger::FATAL).stream()
#define LOG_SYSERR muduo::Logger(__FILE__, __LINE__, false).stream()
#define LOG_SYSFATAL muduo::Logger(__FILE__, __LINE__, true).stream()
使用方法如下:LOG_DEBUG<<"123"; 宏替换为Logger(__FILE__, __LINE__, muduo::Logger::DEBUG, __func__).stream()<<"123"
整体流程为先构造一个匿名的Logger类,调用Logger的构造函数,而Logger的构造函数又调用Impl的构造函数,Impl在构造函数中将,__FILE__ __LINE__等信息添加到LogStream对象的缓冲区中,然后返回LogStream对象,因为LogStream重载了<<操作符,所以<<"123"也是将其加入缓冲区中,当语句结束后,调用Logger的析构函数,将缓冲区的内容刷新到文件或者屏幕中。