前言
目前java世界里的日志框架很多,占主流的是下面四个:
- Log4j/Log4j2: Apache Log4j是一个基于Java的日志记录工具。它是由Ceki Gülcü首创的,现在则是Apache软件基金会的一个项目。 Log4j是几种Java日志框架之一。
- Commons Logging: Apache基金会所属的项目,是一套Java日志接口,之前叫Jakarta Commons Logging,后更名为Commons Logging。
- Slf4j:类似于Commons Logging,是一套简易Java日志门面,本身并无日志的实现。(Simple Logging Facade for Java,缩写Slf4j)。
- Jul:(Java Util Logging),自Java1.4以来的官方日志实现。
事实上这造成了一种java日志系统混乱,没有标准的现状,不过从目前的市场情况上看,slf4j应用更加广泛,因此更加建议在新项目中使用slf4j作为统一日志标准。
在实际选择日志框架的时候,我比较建议按照如下两种场景选择:
- 开发公共模块包:使用jul,由于是jdk自带的日志框架,使用这个日志框架的最大好处在于不用引入新的依赖,不会对依赖这个模块包的程序使用的日志框架有限制,另外,外部如果使用slf4j日志框架的话,还能在外边做桥接,在不改代码的情况下让日志自动输出到slf4j上。
- 开发项目:开发项目的时候,建议使用slf4j作为日志标准,使用logback作为实现。
本文主要介绍如何使用jul,并附上如何在外部将jul桥接到slf4j上。
Java util logging
jul是jdk1.4版本以后的日志框架,放在java.util.logging
包中。
创建日志对象
获取一个日志对象:
import java.util.logging.Logger;
// 假设我们定义了一个类MyLogger.java
private final static Logger LOGGER = Logger.getLogger(MyLogger.class.getName());
这里创建的日志对象实际上是一个有层级的日志对象,通过.
划分层级。
假设创建了一个name
为com.example
的logger,那么这个logger是com
这个logger的子,而com
是空字符串""
的子。
我们可以通过设置父logger的属性来让子logger自动继承。
如果想要设置全局的属性,一般设置名为空字符串的logger即可。
日志等级
jul中定义了如下几个等级的日志(从上到下等级递减):
- SEVERE (最高级)
- WARNING
- INFO
- CONFIG
- FINE
- FINER
- FINEST
另外,jul还定义了OFF
和ALL
两个等级,分别表示全部关闭和全部打开。
在slf4j中,只有五个等级:TRACE, DEBUG, INFO, WARN, ERROR
slf4j等级 jul等级 TRACE ALL,FINEST DEBUG FINER, FINE, CONFIG INFO CONFIG, INFO WARN WARNING ERROR SEVERE, OFF
设置日志等级方式如下:
LOGGER.setLevel(Level.INFO);
这里设置的等级会被子日志对象继承。
Handler
每一个日志对象都可以设置多个处理器java.util.logging.Handler
。
每一个handler
都会收到日志消息,并将日志写到对应的目标,比如控制台或者文件,当然也能定制写到网络或者其他地方。
每个handler
可以通过getLevel()
和setLevel(Level)
来设置自己处理的日志级别。
在jdk中已经默认实现了两个handler
:
- ConsoleHandler: 写到控制台
- FileHandler: 写到文件
日志的jdk默认配置会将INFO级别的日志交给ConsoleHandler处理。
Formatter
每一个handler
可以设置一个格式化器,将日志内容格式化输出(如转换成html格式,或者增加时间信息等)。
jdk默认提供了两个格式化器:
- SimpleFormatter: 普通文本输出
- XMLFormatter: 格式化成xml输出
当然我们可以实现自己的格式化器,只要继承抽象类java.util.logging.Formatter
并实现抽象接口String format(LogRecord record)
,然后设置为handler
的格式化器即可,如:
ConsoleHandler h = new ConsoleHandler();
Formatter f = new Formatter(){
@Override
public String format(LogRecord record){
return record.getMessage();
}
};
handler.setFormatter(f);
LOGGER.addHandler(h);
java.util.logging.LogRecord
是日志信息的封装对象。
Log Manager
jul中,有一个重要的类java.util.logging.LogManager
,它主管logger对象的创建和配置管理。
我们可以通过这个类来设置所有logger的配置,如:
// 设置`com.example`的logger的等级为FINE,则所有对应的子logger的等级都会变成FINE
LogManager.getLogManager().getLogger("").setLevel(Level.FINE);
LogManager对象还能读取配置文件的信息(一般在程序启动时执行)完成对生成的日志对象的配置,使用如下两个接口:
LogManager.getLogManager().readConfiguration(); (1)
LogManager.getLogManager().readConfiguration(InputStream is); (2)
- (1)
默认情况下,先检查系统参数java.util.logging.config.file
指定的文件作为配置文件,如果没有知道,则读取{JAVA_HOME}/lib/logging.properties
作为配置文件。
- (2)
使用自己指定的输入流作为配置文件。
Configure
jul的配置文件是一个properties文件,这里简单介绍几个常用的配置项:
# 默认的handler,多个的话用逗号分隔
handlers= java.util.logging.ConsoleHandler
# 全局默认的日志等级
.level= INFO
# 前面指定的handler的处理日志等级和格式化器
java.util.logging.ConsoleHandler.level = ALL
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
外部使用slf4j重定向jul的日志输出到slf4j
当我们使用的依赖包使用jul来输入日志时,我们可以通过slf4j的外部配置,来将日志全部重定向到slf4j的接口,以便统一日志。
增加maven依赖:
<!-- slf4j的接口 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<!-- jul桥接包 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
<version>1.7.25</version>
</dependency>
启用jul重定向到slf4j
需要使用如下代码:
// 重置所有配置,使得依赖包内部代码对日志的配置失效。
LogManager.getLogManager().reset();
// 清理所有jul的handler
SLF4JBridgeHandler.removeHandlersForRootLogger();
// 安装slf4j的handler
SLF4JBridgeHandler.install();
// 非必需:设置jul的日志级别,如果不设置的话,内部代码日志级别低于INFO的都默认不会输出
LogManager.getLogManager().getLogger("").setLevel(Level.FINEST);