日志系统诞生记

    |     2018年1月23日   |   Java技术, 输入输出(IO)操作   |     0 条评论   |    1349

Java 帝国一出生就带了集合、线程、IO、网络,从 C/C++ 领地拉来大批程序员,却有意无意漏掉一件大事:输出日志。程序跑起来是个黑盒,和预期不符时,单步调试费时且只能在开发机上用;生产环境更只能靠日志定位。

IO 大臣的小算盘是:System.out.println 和 System.err.println 不就够了?于是一直到第三代国王,JDK 都没官方日志包,控制台被打印撑成垃圾场。

一、张家村的需求

张家村电商也逃不过。老村长烦透了难读的 System.out,把小张叫来,要一套通用日志。小张刚从消息队列和 JMS 里练过设计,不想再只做 CRUD。

  • 消息除了控制台,还能进文件、甚至邮件(生产出错)。
  • 内容可格式化:纯文本、XML、HTML。
  • 按 class、package、级别送到不同文件:com.foo → foo.log,com.bar → bar.log,ERROR → errors.log。
  • 要分级:debug 只给本机/测试,error 在生产必须留下。

二、小张抽出的核心概念

先要有事件:LoggingEvent,至少带时间戳和消息。输出地抽象成 Appender(不断追加),而不是 LogDestination。

log1

格式化则是 Formatter 接口:

log2

Appender 引用 Formatter,先格式化 LoggingEvent 再送出。

第三条最难:不同 class/package 目的地不同。不能让每个类直接绑 Appender。从用户角度,取日志先拿到一个 Logger,获取时传入类名或包名:

log3

Logger 再关联一个或多个 Appender,同一条消息就能打到多处:

log4

核心类图:

log5

级别用 Priority:DEBUG < INFO < WARN < ERROR < FATAL。Logger 上提供 debug/info/warn 等方法。Appender 和 Logger 都可以挂一个 Priority:用户打 DEBUG,而 FileAppender 是 ERROR,这条就不必在该 Appender 输出。

三、正交性

村长点破:把 Logger、Appender、Formatter 看成 X/Y/Z 轴,三者能独立变化、任意组合,而不是意大利面。

log6

名字定为 Log4j(Log for Java)。小张又花两个月做出来,性能和设计让很多村镇用上;开源到 Apache 后测试和扩展跟上,成了最流行的日志工具。帝国官僚拒绝把它收进 JDK。IO 大臣只好推动官方另做一套。

四、JUL、Logback 与 SLF4J

JDK 1.4 给出 java.util.logging,核心也是 Logger、Formatter、Handler,和 Log4j 很像,但来晚了。小张后来又写了更快的 logback。选择多了(JUL、log4j、logback、tinylog),切换成本就成问题。

于是有了抽象层 SLF4J(Simple Logging Facade for Java):业务只调门面 API,底下接谁无所谓。

log7

Log4j、JDK logging、tinylog 需要适配层;Logback 直接实现 SLF4J,少一跳,SLF4J + Logback 大有超过 Apache Common Logging + Log4j 之势。

组件 职责
LoggingEvent 一条日志事件:时间戳 + 消息
Appender 目的地:控制台 / 文件 / 邮件
Formatter 把事件格式化成文本、XML、HTML
Logger 按类名/包名获取,绑定一个或多个 Appender
Priority DEBUG…FATAL,在 Logger 和 Appender 上过滤
SLF4J 门面,底下可换实现

后记:小张即 Ceki Gülcü,Log4j、logback、SLF4J 都出自他。原文:刘欣,码农翻身(ID:coderising)。

一句话总结:日志系统能活下来,是因为 Logger、Appender、Formatter 三个维度正交;级别在 Logger 和 Appender 上各过滤一次。今天业务应依赖 SLF4J,实现可选 Logback 或 Log4j。

转载请注明来源:日志系统诞生记
本文链接地址:https://ai.zhousir.top/?p=2193
回复 取消