序言
对一个服务端程序来说,日志是用于发现系统问题,诊断系统运行情况的一个重要工具,所以日志库的设计要以帮助跟踪程序运行状态为宗旨,这篇文章来源于最近我在一个通信协议库中所写的一个微型的日志组件,总共也就两百来行代码,实现了日志的搜集、过滤、输出功能。
日志库的功能与设计
一个日志库,应该把来自于程序各个部分的日志信息搜集起来,按照一定的过滤规则(通常是按日志级别过滤),将通过过滤的日志信息输出到指定的目标地点,可以是终端控制台,也可以是磁盘文件或者网络,甚至可以是它们的组合。
下面是一条日志信息:
<2015-08-21 09:51:46.939797> [trace] [tcp-client-124658]
receive 11 bytes data from: [127.0.0.1]:2404
68 09 96 00 bc 00 43 f8 ad 5c 88
一条日志输出,主要包括时间戳、日志级别、标记、日志内容这些信息。
日志搜集器
日志搜集器的作用是为每一条日志打上相应的标记,这个标记可以是程序中某个类的名字,也可以是某个容器的名字,总之可以是你想要在最终的日志文件中进行跟踪的一个关键字。程序中在需要输出日志信息的地方,指定一个搜集器,也就为该条日志打上了对应的标记。
日志级别与过滤器
日志信息通常是分等级的,一般情况下包含普通信息、警告、错误、致命异常这些级别,并且等级由低到高,以区分日志信息的重要程度。
过滤器给了应用层控制日志量的机会,如果系统日志量过大,可以通过抬高日志的级别来加以控制,所以常见的过滤器就是按级别过滤,只有大于等于某个指定级别的日志才会真的被输出。
日志输出器
日志信息最终都要保存到某个存储设备,例如文件或者网络,通常也可以输出到标准控制台,在日志库中,只有它才真正关心日志该怎样输出到存储设备的,通常它要考虑的主题是缓冲区和多线程安全。
如果输出到终端,可以使用显眼的颜色来输出错误信息,以使人注意到它的出现。可以同时指定多个输出地,比如同时输出到控制台和文件,也可以为不同的输出地设置不同的过滤器,比如控制台显示全部日志信息,但是只将错误信息写入到文件,本文要实现的日志库没有考虑这个功能,要实现也只要稍加修改即可。
日志管理器
前面的日志搜集器、过滤器、输出器都只是日志库的重要零件,得有一个管理器把它们集成起来,管理器的作用就是提供打印日志的接口,以接受来自程序各个角落的日志输出请求,并用过滤器对日志信息进行过滤,将能通过过滤器的日志信息传递到输出器进行存储。
日志库的实现
日志搜集器
日志搜集器的作用就是保存标记,可以为程序的每个模块设置一个日志搜集器,该模块打印的日志都交给这个搜集器,以便带上这个搜集器的标记,例如在上一节中所展示的日志信息示例,[tcp-client-124658] 就代表这是某个 TCP 客户端所打印的日志。
它的实现如下:
// 负责搜集日志,含有日志标签等信息
class log_collector
{
public:
const std::string name_;
public:
explicit log_collector(const std::string& name) : name_(name) {}
};
日志级别
这个就更简单了:
class log_level
{
public:
const int level_;
const std::string name_;
pub