对于一个应用程序来说日志记录是必不可少的一部分,不仅线上问题追踪,基于日志的业务逻辑统计分析离不日志,按约定编写日志,对于团队协同开发也是至关重要的。
“队友看不懂你的代码”这绝对正确的假设。那如何把集成测试或开发中发现问题相关详细信息告诉问题人(异常输出常是天书),而不要两人坐在一起联合调试?利用日志,打印出业务类(Java 约定每个类都是唯一负责人的)的重要信息是最有价值的,通过研究业务类之间协同关系,调试者就能快速理解业务场景上下文(Context),发现问题所在。
现今,Java日志领域被划分为两大阵营:Commons Logging阵营和SLF4J阵营。Commons Logging在Apache大树的笼罩下,有很大的用户基数。但有证据表明,形式正在发生变化。2013年底有人分析了GitHub上30000个项目,统计出了最流行的100个Libraries,可以看出slf4j的发展趋势更好:
junit 和 slf4j 真是好 CP。即正规开发团队绝对多数成对使用两个包,初学者或临时项目两者都不用。
Apache眼看有被Logback反超的势头,于2012-07重写了log4j 1.x,成立了新的项目Log4j 2。Log4j 2具有logback的所有特性。
(1)建立包依赖
打开 pom.xml (如 hello world 程序),在 dependencies 下添加
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>1.2.17version>
dependency>
存盘后,很快就完成了下载,添加的外部库就有了。
(2)配置日志输出
在 main 下创建 resources 目录,创建 log 4.properties 文本文件2:
#可以设置级别:debug < info < warn < error
#debug: 显示debug, info, warn, error
#info: 显示info, warn, error
#warn: 显示warn, error
#error: 只显示error
#日志的输出级别由rootLogger和普通Logger设置的最高级别决定。
#log4j.rootLogger=debug,appender1
#log4j.rootLogger=info,appender1
log4j.rootLogger=warn,appender1
#log4j.rootLogger=error,appender1
#选择以下日志输出类
#org.apache.log4j.ConsoleAppender(控制台),
#org.apache.log4j.FileAppender(文件),
#org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件),
#org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件),
#org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)
#输出到控制台
log4j.appender.appender1=org.apache.log4j.ConsoleAppender
#样式为TTCCLayout
log4j.appender.appender1.layout=org.apache.log4j.TTCCLayout
#这里配置的是类所在的包test.log4j, 逗号之前未配置日志输出级别,默认为根logger的级别
log4j.logger.test.log4j=, TEST
log4j.appender.TEST=org.apache.log4j.ConsoleAppender
log4j.appender.TEST.layout=org.apache.log4j.TTCCLayout
如果你要自定义输出个格式,建议阅读3。
(3)日志输出
程序如图:
编程中 IDE 问题:
Alt + Enter
选择 import org.apache.log4j.Logger
。注意:需要做日志的类,一定在开始生成静态的 logger 对象,参数一定是这个类的类型。
输出结果:
Hello World!
[main] WARN com.mycompany.helloworld.App - This is warn message
[main] ERROR com.mycompany.helloworld.App - This is error message
Simple Logging Facade for Java (SLF4J) 作为其他各种日志框架的门面(Facade)或抽象(Abstraction),为应用程序提供一致的服务。可在部署期间再选择与环境匹配的日志实现。
SLF4J 没有实现日志配置与输出,只实现了应用程序访问日志的门面,官方4图表达的很清晰:
SLF4J 相当成功,这意味程序员不需要学习任何日志的知识,仅需要掌握 SLF4J 就够了。具体日志的实现与配置,拿来就用。 因此,它获取了与 Junit 类似的地位,成为事实的日志标准。
(1)SLF4J 配置
看上图,我们用 slf4j 做日志标准,log4j1.2 做实现,则 pom.xml 配置如下:
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-log4j12artifactId>
<version>1.7.21version>
dependency>
使用上图第三排作为依赖,应用会自动添加所有正确的依赖。
(2)日志输出
程序如图:
程序中在也没有与 log4j 相关的类了,LoggerFactory 是生产日志类的工程(Factory Pattern/设计模式),Java 程序员看了多很亲切。关键是输出结果一样!!!
输出结果:
Hello World!
[main] WARN com.mycompany.helloworld.App - This is waring message
[main] ERROR com.mycompany.helloworld.App - This is error message
slf4j 输出是与 print 语句一致的,程序员感觉很好。
日志记录器(Logger)的可用级别Level (不包括自定义级别 Level), 以下内容就是摘自log4j API (5“>http://jakarta.apache.org/log4j/docs/api/index.html)6,这里仅列举了程序中用到的四种:
日志记录器(Logger)的行为是分等级的。分为OFF、FATAL、ERROR、WARN、INFO、DEBUG、TRACE、ALL或者您定义的级别。制到应用程序中相应级别的日志信息的开关。比如在这里定义了INFO级别,则应用程序中所有DEBUG级别的日志信息将不被打印出来。程序会打印高于或等于所设置级别的日志,设置的日志等级越高,打印出来的日志就越少。
(1)个体调试
个体开发通常只需要 debug, warn, error。
Debug 就是程序员查看程序的执行过程工具,例如,执行 sql 前打印 SQL 语句等;
Warn 就是程序员检查输入和关键点数据脱离期望的位置的输出,例如,输入为 null 等;
Error 几乎就是异常处理位置的输出。例如,连接异常断开等。
程序员通常这样做细粒度配置 log4j ,例如:
log4j.logger.org.apache=warn
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.Connection=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
这时 jdbc 这些类的细节都打印出来,这对分析局部数据访问问题是绝对有效的。
(2)协同调试
对于分布式开发的程序员,给出上述信息要么太多,要么不够。这些程序员需要什么样的信息支持调试呢?如果知道一些软件设计,如设计的如下顺序图(网上下载的图,不代表作者观点)
这个设计图反映了控制器、业务服务、数据存取类协同过程。通常,这些类是不同程序员实现的。
现在,数据库中有用户信息,但如何用日志显示登陆过程中对象协作过程呢? Info 信息闪亮登场了
通常我们在这些对象公有方法的开始,例如在登陆服务登陆方法的开始:
logger.info("Enter login with user={} and key={}",user.name, user.key);
在测试与调试阶段,通过合适的日志配置,例如:
log4j.rootLogger=warn,appender1
...
log4j.logger.com.mycompany.myproject=info
日志输出则得到如设计图要求的对象协作过程,以及其中的关键上下文信息。
对 粗粒度级别 信息的理解不是概念,而是持续构建实践的关键之一。
你必须知道程序开发是创造性劳动。即程序员和摄影师、画家一样,有了好的 ideal,必须在最短时间内写完,根本没太多时间考虑测试,日志等后期工作。如同摄影师,在街头就是迅速构思并完成拍照,美化等交给 PS 后期。但是,没有好的后期工作,保证好产品也很难。
本文描述了日志门面 slj4f 和 log4j 的使用,重点讲述了日志在产品每日构建中的重要作用。其中,很多编程约定是程序员必须遵守的,约定胜于配置!
本文也涉及了一些设计模式,例如:门面模式、工厂模式。了解这些模式,对中级编程至关重要。
【参考】