Java Log Frameworks
常见的java日志框架和实现主要有:
- slf4j
- log4j
- logback
- apache jakarta commons-logging
- java.util.logging
- log4j 2
上述几个日志工具的简单说明:
- 前面3个日志工具是同一个作者 Ceki Gülcü 开发的。
slf4j是Simple Logging Facade for Java的缩写,主要是在slf4j-api中定义了日志接口和工厂方法,它并不具体实现日志操作,平时项目开发中引用的日志相关的代码都应该来自这个包。logback-classic是slf4j的完整实现,目前有取代log4j的趋势,spring-boot项目就在使用这个日志框架。slf4j-log4j12是连接slf4j-api和log4j的适配器,它实现了slf4j-api中StaticLoggerBinder接口,从而使得在编译时绑定的是slf4j-log4j12的getSingleton()方法。- apache的
JCL在较早的一些类库中用得较多,后来更多第三方类库采用log4j,比如struts、spring和hibernate。 java.util.logging是JDK自带的,日志等级较多,较少使用。log4j 2在多线程模型中性能远高于其他日志框架。
Logger LEVEL
slf4j中定义的常用的日志级别:
- ERROR
- WARN
- INFO
- DEBUG
另外还有一个比DEBUG还详细的TRACE,主要是用来跟踪一些非常复杂的算法中,在每一步计算过程中输出详细的结果,用于跟踪分析,并且代码完成后应该将之移除,不要上传到代码库,因此下面就不做说明了。
日志级别使用场景
DEBUG
- 开发调试使用
- 信息面向开发工程师
INFO
- 对象状态变化前后
- API方法调用前显示传入的参数列表
- API方法返回的关键信息
- 定时任务的开始结束信息
- 关键方法的进入退出点及相应参数
- 代码块运行时间监控,性能分析
- 信息面向开发工程师及运维人员
WARN
- 不影响程序运行的配置问题
- 可恢复的异常
- 信息面向开发工程师及运维人员
ERROR
- 运行时异常
- 无法处理的异常
- 记录异常信息和堆栈,不要吞没异常
- Logger不是异常处理的工具
- 信息面向开发工程师及运维人员
不同的运行环境使用不同的日志等级
- production - INFO
- development/stage - DEBUG
- test - DEBUG
Should logger object static or not
- 建议使用
private static final形式,每个类所有对象共用一个日志对象即可,KISS。 non-static被子类继承的优势,只要在父类里声明一个protected的日志对象,子类就可以直接使用,缺点是每个子类对象创建都会生成一个日志对象。non-static是IOC友好的,因为IOC中的BEAN一般都是单例的。
STATIC
private static final Logger LOGGER = LoggerFactory.getLogger(LoggerExample.class); |
INSTANCE
private final Logger logger = LoggerFactory.getLogger(this.getClass()); |
日志查询RequstID
如果应用是分布式的,或者是用户会访问多个子系统的,当访问者第一次进入系统时,可以为其生成一个统一的RequestID,并使用MDC(Mapped Diagnostic Context,映射调试上下文)写入日志文件中,可以跟踪用户完整的操作过程,对问题分析和数据分析会很有用。
比如使用如下这些信息通过一个可逆算法生成这个RequestID。
- JSESSIONID
- USERID
- TIMESTAMP
使用结构化或者半结构化的日志消息
日志格式设计比较好的日志消息,对于查找问题会很有利,也便于使用logstash导入到elasticsearch中进行分析,或者是kibana查看。
日志命名格式以及日志文件名
日志文件一般按日期,应用标识和日志级别三者来命名,也可以再带上主机名。如:web-info-2016-09-18.log。
Logger使用注意事项
不要使用字符串拼接方法
log.debug("orderId is " + order.getId() + " and amounts is " + amounts); |
使用对象占位符
log.debug("orderId is {} and amounts is {}", order.getId(), amounts); |
记录异常信息
try { ...} catch (Exception e) { e.printStackTrace(); // BAD log.error("IO exception", e.getMessage()); // BAD log.error("IO exception", e); // OK} |
不要记录异常并抛出异常
try { ...} catch (Exception e) { log.error("IO exception"); // OK log.error("IO exception", e); // BAD throw e;} |
不要记录集合和复杂对象
log.info("fetch users: {} from db.", users); |
程序中不要使用System.out
System.out.println("current login user name is : " + user.getName()); |