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);
INSTANCEprivate 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(); // BADlog.error("IO exception", e.getMessage()); // BADlog.error("IO exception", e); // OK}
不要记录异常并抛出异常try {...} catch (Exception e) {log.error("IO exception"); // OKlog.error("IO exception", e); // BADthrow e;}
不要记录集合和复杂对象log.info("fetch users: {} from db.", users);
程序中不要使用System.outSystem.out.println("current login user name is : " + user.getName());
References