Linux 多线程服务端编程笔记(五)
第五章 高效的多线程日志
1、 两种日志
- 交易日志
- 诊断日志
2、 关键进程记录的日志
日志通常需要记录:
- 收到的每条内部消息的 ID、关键字段、长度、hash 值等。
- 收到的每条外部消息的全文。
- 发送消息的全文,每条消息都有全局唯一的id
- 关键内部状态的变更
另外:
- 每条日志都有时间戳
- 一个日志库大致分为:前端 - 生成日志;后端 - 把日志写到目的地。异步日志
3、日志库应该提供的功能
- 日志消息级别:TRACE、DEBUG、INFO、WARN、ERROR、FAEAL
- 日志消息可能有多个目的地,如文件,socket,SMTP。对于分布式系统服务进程而言,目的地只有本地文件
- 日志消息可以配置
- 可以设置运行时过哭泣
4、 日志消息的几个要点
- 尽量每条 日志占一行,方便分析
- 时间戳精确到微妙,gettimieofday()
- 使用GMT时区
- 打印线程id
- 打印日记级别
- 打印原文件名和行号
5、多线程异步日志
-
线程安全的多线程日志的解决思路
- 用一个全局锁保护IO,或者每个线程单独写一个日志文件。性能堪忧,前者造成所有线程抢占一个锁,后者会让业务线程阻塞在写磁盘操作上
- 每个进程只写一个日志文件,用一个背景线程负责收集日志消息,并写入日志文件,其他业务线程只需往这个日志线程中发送日志消息,称为“异步日志”
-
我们需要一个“队列”将日志前端的数据传送到后端(日志线程)
-
muduo日志库采用的是双缓冲技术
基本思路是准备两块 buffer:A 和 B,前端负责往 buffer A 填写数据,后端负责将 buffer B 的数据写入文件。buffer A 写满就交换 A 和 B。如此往复。
-
日志消息堆积
-
前端陷入死循环,拼命发送日志消息,超过后端的处理能力
-
muduo日志处理日志堆积的方法:直接丢到多余的buffer以腾出内存
if (buffersToWrite.size() > 25) { char buf[256]; snprintf(buf, sizeof buf, "Dropped log messages at %s, %zd larger buffers\n", Timestamp::now().toFormattedString().c_str(), buffersToWrite.size()-2); fputs(buf, stderr); output.append(buf, static_cast<int>(strlen(buf))); buffersToWrite.erase(buffersToWrite.begin()+2, buffersToWrite.end()); }
-
5、日志的其他方案
- 使用队列前后端中传递消息,每一个消息都是一个std::string,这种消息都要分配内存,并由后端线程释放
- muduo现在的异步日志实现用了一个全局锁。尽馆临界区很少,但是如果线程数目较多,锁的争用也可能影响性能