第5章 高效的多线程日志

2018-09-27  本文已影响0人  Junior888

日志有两个意思:

1. 功能需求

常规的通用日志库如log4j13/ logback14 通常会提供丰富的功能,但这些功能不一定全都是必需的。

  1. 日志 消息 有多 种 级别( level), 如 TRACE、 DEBUG、 INFO、 WARN、 ERROR、 FATAL 等。
  2. 日志 消息 可能有 多个 目的地( appender), 如 文件、 socket、 SMTP 等。
  3. 日志 消息 的 格式 可 配置( layout), 例如 org. apache. log4j. PatternLayout。 4. 可以 设置 运行时 过滤器( filter), 控制 不同 组件 的 日志 消息 的 级别 和 目的地。

在上面 这 几项 中, 我 认为 除了 第一 项 之外, 其余 三项 都是 非 必需 的 功能。

日志 的 输出 级别 在 运行时 可调, 这样 同一个 可执行 文件 可以分 别在 QA 测试 环境 的 时候 输出 DEBUG 级别 的 日志, 在 生产 环境 输出 INFO 级别 的 日志。 在必 要的 时候 也可以 临 时在 线 调整 日志 的 输出 级别。

例如 某台 机器 的 消息 量过 大、 日志 文件 太多、 磁盘 空间 紧张, 那么 可以 临时 调整 为 WARNING 级别 输出, 减少 日志 数目。 又比 如 某个 新 上 线 的 进程 的 行为 略显古怪,则可以来临时调整为DEBUG级别输出,打印更细节的日志消息一遍分析查错。调整日志的输出级别不需要重新翻译,也不需要重启进程,主要调用muduo::Logger::setLogLevel()就能即时生效。

有几个简单的技巧:

往文件写日志的一个常见问题就是,万一程序崩溃,那么最后若干条日志往往就丢失了,因为日志库不能每条信息都flush硬盘,更不能每条日志都open/close文件,这样开销太大。muduo采用的做法是:

日志消息的格式主要有以下几个要素:

2. 性能需求

高效性体现在几方面:

3. 多线程异步日志

多线程程序对日志库提出了新的需求:线程安全,即多个线程可以并发写日志,两个线程的日志消息不会出现交织。

线程安全不难办到,简单的办法就是用一个全局的mutex保护IO,活着每个线程单独写一个日志文件,这两种做法会损失高效性。前者会造成全部线程抢一个锁,后者有可能让业务线程阻塞在写磁盘操作上。

一个多线的程序的每个进程最好只写一个日志文件,这样分析日志更方便。再说多线程写多个文件也不一定能够提速。解决办法可以用一个背景线程负责收集日志消息,并写入日志文件,其他业务线程只管往这个“日志线程”发送日志消息,这称为“异步日志”。

在多线程服务程序中,异步日志是必须的,因为如果在网络IO线程或业务线程中直接往磁盘写数据的话,写操作偶尔可能阻塞长达数秒之久。这可能导致请求方超时,活着耽误发送心跳消息,在分布式系统中更可能造成多米诺骨牌效应,例如误报死锁引发自动failover等。因此,正常的实时业务处理中,应该彻底避免磁盘IO,这里使用one loop per thread模型的非阻塞服务程序中尤为重要。

上一篇 下一篇

猜你喜欢

热点阅读