前言
Java帝国在诞生之初就提供了集合、线程、IO、网络等常用功能,从C和C++领地那里吸引了大量程序员过来加盟,但是却有意无意地忽略了一个重要的功能: 输出日志。
对于这一点,IO大臣其实非常清楚, 日志是个很重要的东西, 因为程序运行起来以后, 基本上就是一个黑盒子,如果程序的行为和预料的不一致,那就是出现Bug了,如何去定位这个Bug 呢?
臣民们能用的工具有两个,第一个就是单步调试,一步步地跟踪,查看代码中变量的值, 这种办法费时费力, 并且只能在程序员的机器上才能用。
第二种就是在特定的地方打印日志, 通过日志的输出,帮助快速定位。尤其是当代码在生产环境上跑起来以后, 日志信息更是必不可少,要不然出了状况两眼一抹黑,上哪儿找问题去? 总不能让臣民们把自己变成一个线程进入系统来执行吧?
但是IO大臣也有自己的小算盘: 日志嘛, 用我的System.out.println(…..) 不就可以了?! 我还提供了System.err.println不是?
在IO大臣的阻挠下, 从帝国的第一代国王到第三代国王, 都没有在JDK中提供日志相关的工具包, 臣民们只好忍受着去使用System.out.println去输出日志,把所有的信息都输出到控制台, 让那里变成一堆垃圾。
张家村
张家村的电子商务系统也不能幸免,自然也遇到了日志的问题。经验丰富的老村长已经烦透了System.out.println所输出的大量难于理解的无用信息,看着村民民整天苦逼地和这些System.out做斗争,他找来了小张,命令他设计一个通用的处理日志的系统。
小张在消息队列和JMS的设计上花了不少功夫, 积累了丰富的经验,从那以后一直都是实现业务代码,一直都是CRUD, 张二妮整天笑话自己是HTML填空人员,这一回一定要让她看看自己的设计功力!
老村长给小张下达的需求是这样的:
1. 日志消息除了能打印到控制台, 还可以输出到文件,甚至可以通过邮件发送出去(例如生成环境出错的消息)
2. 日志内容应该可以做格式化, 例如变成纯文本,XML, HTML格式等等
3. 对于不同的Java class,不同的 package , 还有不同级别的日志,应该可以灵活地输出到不同的文件中。
例如对于com.foo 这个package,所有的日志都输出到 foo.log 文件中
对于com.bar 这个package ,所有文件都输出到bar. log文件中
对于所有的ERROR级别的日志,都输出到 errors.log文件中
4. 能对日志进行分级, 有些日志纯属debug , 在本机或者测试环境使用, 方便程序员的调试, 生产环境完全不需要。有些日志是描述错误(error)的, 在生产环境下出错的话必须要记录下来,帮助后续的分析。
小张仔细看了看,拍着胸脯对老村长说:“没问题, 明天一定让您老看到结果。”
小张的设计
老村长走了以后,小张开始分析需求, 祭出“面向对象设计大法”,试图从村长的需求中抽象出一点概念。
首先要记录日志,肯定需要一个类来表达日志的概念,这个类至少应该有两个属性,一个是时间戳,一个是消息本身,把它叫做 LoggingEvent 吧,记录日志就像记录一个事件嘛。
其次是日志可以输出到不同的地方,控制台、文件、邮件等等, 这个可以抽象一下,不就是写到不同的目的地吗? 可以叫做LogDestination?
嗯, 还是简单一点,叫做 Appender 吧, 暗含了可以不断追加日志的意思。
至于第二条的日志内容可以格式化,完全可以比葫芦画瓢, 定义一个Formatter接口去格式化消息。
对了, Appender 应该引用Formatter ,这样以来就可以对LoggingEvent记录格式化以后再发送。
第三条需求把小张给难住了,不同的class, package 输出的目的地不同? “目的地”这个概念是由Appender来表达的, 难道让不同的class, package 和Appender关联? 不不, 不能这样 !
还需要一个新的概念 , 这个概念是什么?
从用户角度想一下, 村民们要想获取日志,必须得先获取个什么东西,这个东西是不是可以称为Logger啊? 灵感的火花就闪了那么一下就被小张抓住了: 获取Logger的时候要传入类名或者包名!