简单总结一下muduo中的日志系统的功能运作方式及一些疑问。
第一个类:
template<int SIZE> class FixedBuffer
成员变量:
1. 成员函数: 一个函数类型的成员 void (*cookie_)(),用来给内存块做cookie。
2. 一个缓冲区data_[SIZE], 其缓冲区大小由类模板参数决定(这里是一个非类型模板参数)。
3. 一个指示缓冲区当前位置的指针 char* cur_;
功能:
1.缓冲区的管理,包括缓冲区的各种参数的获取,将新内容写入缓冲区等
void append(const char* buf, size_t len) { //如果 剩余的空间还够用 if(implicit_cast<size_t>(avail()) > len){ //这是google提供的一个类型转换函数 有什么不同呢 //将内容加入缓冲区 memcpy(cur_, buf, len); cur_ += len; } }
从函数代码可知,往缓冲区写入数据提供的第一级的写入函数,只接受一个char类型的缓冲区以及缓冲区长度。后续如果要写入这个缓冲区就需要先转换为这种格式。
2. 给内存块加cookie。
加cookie的目的主要是为了当程序崩溃的时候,还没来得及写入的日志可以通过dump core文件搜索cookie得到。
实现的形式是 在类的构造函数中将函数的地址赋给cookie变量 在析构函数中可进行同样的操作。
总结:这个类就是实现缓冲区的生成,各种基本功能函数的。
第二个类:
class LogStream
这个类是实行流控制的类。
成员变量:
1. 一个FixedBuffer类型的类,相当于管理了一个内存块。
2. 一个static 的变量, static const omt kMaxNumericSize = 32; 这个变量记录变量记录的最大的有效数字的位数。
功能: 主要实现的功能是 将各种类型的数据类型进行转换使其可以非常方便的被写入到缓冲区中。
下面挑几个典型的函数实现进行说明:
LogStream& LogStream::operator<<(short v) { *this << static_cast<int>(v); return *this; }
这个将 short转换成了 int 然后再调用int对应的<<运算符。
int对应的<<运算符:
LogStream& LogStream::operator<<(int v) { formatInteger(v); return *this; }
之前说过FixedBuffer缓冲区写入数据的格式是特定的,以上存在一个转换函数:formatInteger
formatInteger函数:
template<typename T> void LogStream::formatInteger(T v) { if(buffer_.avail() >= kMaxNumericSize) { size_t len = convert(buffer_.current(), v); buffer_.add(len); //改变当前位置指针标志 } }
这里又用到了convert函数:
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]; }while(i != 0); if(value < 0) { *p++ = ‘-‘; } *p = ‘\0‘; std::reverse(buf, p); return p - buf; //返回新加入的字符串的长度 } // end of convert
就是将一个数字转化为字符串类型并写入到缓冲区中
通过上面的一串操作完善了重载操作符 << 的功能:
其它数据类型 ----》int类型-----》formatInteger函数-------------》调用convert 将int逐位转换为字符类型病添加到缓冲区中
|
|----------》调整缓冲区当前位置计数器
奇怪的是以上的转换过程并没有调用之前预留好的append函数。
第三个类:
通过上面的两个类已经能够实现通过重载操作符的形式将数据写入到缓冲区中,接下来的类要解决的问题是,做一个比较好的写入接口以及将缓冲区里面的数据写入到文件中去。
class Logger
这个类里面自己定义了一个类 搞得很搅,先来看看这个内置的类:
class Impl { public: //日志等级类型信息 typedef Logger::LogLevel LogLevel; Impl(LogLevel level, int old_errno, const SourceFile& file, int line);void finish(); //写入日志缓冲区的流控制 LogStream stream_; //日志等级 LogLevel level_; int line_; //应该是文件名 SourceFile basename_; };
这个类是在Logger中作为私有成员被定义的。 看其名字就知道这个类是Logger的大管家,核心功能都在这个类中实现。
成员变量:
1. stream_ 写入日志缓存的流控制类
2. level_记录日志的等级
3. line_日志发生在源文件的那一行
4. 产生日志的源文件名称, 这个源文件是专门用一个类来管理的,后面再说。
功能:
在构造函数和finish函数中完成了大部分的功能:
构造函数:
写入日志的头部信息 包括线程的进程id 日志级别 等信息
finish函数:
写入输出日志的代码的源文件和在源文件中的行数。like this:
14124 INFO trace - test.cc:15
下面来看看那个管理文件的类:
class SourceFile { public: template<int N> SourceFile(const char (&arr)[N]) //代理了一个缓冲区 : data_(arr), //代理这段缓冲区 size_(N-1) { //为什么要去掉第一个 / 之前的位置呢 ????????? const char* slash = strrchr(data_, ‘/‘); //slash 是 最后一个 / 出现的位置 if(slash) { data_ = slash + 1; } size_ = static_cast<int>(strlen(data_)); } explicit SourceFile(const char* filename) : data_(filename) { const char* slash = strrchr(filename, ‘/‘); if(slash) { data_ = slash + 1; } size_ = static_cast<int>(strlen(data_)); } const char* data_; int size_; }; // SourceFile
成员变量:
1. 一个指向文件名的指针data_,构造函数传入引用,然后再通过引用将首地址赋值给它。 data_ : 可访问待操作的文件。
2. size_文件名的大小
功能:将传入的保存有任意文件路径的 源文件名更改为只剩下源文件名,例如: /home/muduo/test.cc 转化为 ---》test.cc
总结: 这就是一个功能函数,完全由它的构造函数实现了他的功能。
下面开始介绍今天的主角:
class Logger
成员变量:
1. Impl impl_; 大管家类 里面包含了 SourceFile
功能:
构造函数: 初始化了一些成员变量,初始化了impl_ 并在流中写入了一些基本信息。
析构函数:将缓冲区的内容通过注册的输出函数输出到对应的位置,可以是标准输出或文件 根据具体的功能函数决定。
原文:https://www.cnblogs.com/xiongxinxzy/p/13639495.html