一个日志库大体可分为前端和后端2部分:
- 前端是供应应用程序使用的接口API,并生成日志消息
- 后端则负责把日志消息写到目的地
前端大体上有2种API风格:
- C的 printf
- C++的 stream<< 风格
muduo的日志库采用的是 C++的 stream<< 风格,理由有2:
- 不需要费心保持格式字符串与参数类型的一致性
- 输出的日志级别高于语句的日志级别时,打印日志是个空操作,减小了开销。
在多线程程序中,每个线程有自己的前端,整个程序共用一个后端。
但难点在于将日志数据从多个前端高效地传输到后端,这是一个典型地多生产者-单消费者地问题:
对生产者而言,要尽量做到低延迟,低CPU开销,无阻塞。
对消费者而言,要做到足够大地吞吐量,并占用较少资源。
功能需求
日志消息有几个要点:
- 每条日志占一行,方便awk,grep 工具分析
- 避免在日志格式中出现正则表达式中的元字符,理由同上
- 打印线程ID
- 打印日志级别
- 打印源文件和行号
- 时间戳精确到微秒
- 对于分布式系统而言,使用GMT时区
性能需求
- 每秒写
- 能应对一个进程产生大量日志数据的场景
- 不阻塞正常的执行流程
- 在多线程程序中,不造成争用
- 可以瞬时写满磁盘带宽
muduo 日志库实现
多线程程序对日志库提出了新的需求:线程安全。简单的办法就是使用一个mutex保护I/O,但这种做法就不能做到高效。
最好的方法是:用一个线程收集日志消息,并写入日志文件,其他业务线程只负责往这个日志线程发送日志消息,这称为"异步日志"
网友评论