Java日志
日志门面和日志框架实现
日志门面(Facade)把不同的日志系统的实现进行了抽象化,只提供了统一的日志使用接口。由于它只是一个接口,所以最终日志的格式、记录级别、输出方式等都要通过接口绑定的具体日志框架实现。
实际的项目开发中可能会使用到很多不同的框架,而这些框架又使用了不同的日志框架。我们希望所有的框架一律使用日志门面(Slf4j)进行日志打印,这时该怎么去解决?
- logback直接实现了slf4j-api中的接口,通过接口实现的方式完成了门面的实现。
- log4j2没有直接实现接口,需要适配器
log4j-slf4j-impl
Logback
添加logback依赖
1
2
3
4
5
6<!-- logback -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.5.6</version>
</dependency>可见,依赖中包含了对
slf4j-api
的依赖。logback实现了slf4j-api中的接口,通过接口实现的方式完成了门面的实现。用
/resource/logback.xml
进行日志配置。官网Chapter 3: Configuration (qos.ch)
Logback can be configured either programmatically or with a configuration script expressed in XML, Groovy or serialized model format.
1
2
3
4
5
6
7
8
9
10
11
12
13
14<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%p] %c: %m%n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
</configuration>编写日志输出,查看依赖以及输出。
1
2
3
4
5
6
7
8
9
10
11// 导入的是slf4j提供的日志相关类和接口
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Main {
private static final Logger LOGGER = LoggerFactory.getLogger(Main.class);
public static void main(String[] args) {
LOGGER.info("logback的日志打印");
}
}打印效果:
2024-08-07 16:01:10 [INFO] com.hunter.demo.Main: logback的日志打印
log4j2
单独使用Log4j2日志框架
添加log4j2的依赖
1
2
3
4
5
6<!-- log4j 2 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.23.1</version>
</dependency>用
/resource/log4j2.properties
进行日志配置。1
2
3
4
5
6
7rootLogger.level=info
rootLogger.appenderRef.stdout.ref=STDOUT
appender.console.type=Console
appender.console.name=STDOUT
appender.console.layout.type=PatternLayout
appender.console.layout.pattern= %d{yyyy-MM-dd HH:mm:ss} [%p] %c: %m%n编写日志输出,查看依赖以及输出。
1
2
3
4
5
6
7
8
9
10
11// 导入的都是log4j2自己提供的日志相关类和接口
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class Main {
private static final Logger LOGGER = LogManager.getLogger();
public static void main(String[] args) {
LOGGER.info("log4j2的日志打印");
}
}打印效果:
2024-08-07 16:01:10 [INFO] com.hunter.demo.Main: log4j 2的日志打印
绑定slf4j门面接口
使用依赖
log4j-slf4j-impl
1
2
3
4
5
6<!-- 绑定了slf4j日志门面的log4j2日志框架 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j2-impl</artifactId>
<version>2.23.1</version>
</dependency>可见,该依赖中包含了对
slf4j-api
的依赖。/resource/log4j2.properties
进行日志配置。1
2
3
4
5
6
7rootLogger.level=info
rootLogger.appenderRef.stdout.ref=STDOUT
appender.console.type=Console
appender.console.name=STDOUT
appender.console.layout.type=PatternLayout
appender.console.layout.pattern= %d{yyyy-MM-dd HH:mm:ss} [%p] %c: %m%n编写日志输出,查看依赖。
1
2
3
4
5
6
7
8
9
10
11// 导入的是slf4j提供的日志相关类和接口
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Main {
private static final Logger LOGGER = LoggerFactory.getLogger(Main.class);
public static void main(String[] args) {
LOGGER.info("log4j2的日志打印");
}
}
统一日志标准
项目依赖中使用不同的日志框架
- 依赖的项目A使用log4j2框架
- 依赖的项目B使用logback框架
所依赖的项目中的日志可以正常打印(使用各自的日志配置进行打印),不会产生冲突。
如果日志输出的标准不一致,可以通过在当前项目分别定义对应日志框架的日志配置文件,覆盖依赖的项目中的日志配置文件。
项目依赖中都使用slf4j日志门面接口
依赖的项目A使用绑定了slf4j日志门面的log4j2日志框架
A 👉 slf4j 👉 log4j2
依赖的项目B使用logback框架
B 👉 slf4j 👉 logback
这样会使日志门面接口调用实际实现的时候混乱,因为有多个实现类。会根据类加载顺序指定实际使用的日志框架,并读取相应的日志配置文件。
如果有明确要使用的日志框架,且恰好其加载顺序靠后,要如何指定日志框架?
在pom.xml
中,把非目标日志框架的依赖从主动依赖中**exclude
**掉即可。这种情况,日志输出采用的是同一个日志配置文件,所以日志输出标准是一致的。
项目依赖中既有日志框架,又有slf4j日志门面接口
依赖的项目A使用log4j2框架
依赖的项目B使用绑定了slf4j日志门面的log4j2日志框架
B 👉 slf4j 👉 log4j2
依赖的项目C使用logback框架
C 👉 slf4j 👉 logback
这个情况,日志门面接口调用实际实现的时候会混乱、日志框架会使用自己的日志配置进行打印。要统一日志标准,需要两步:
把非目标日志框架的依赖从主动依赖中**
exclude
**掉。exclude
log4j2框架,使依赖中的log4j2也能”实现”slf4j日志门面。1
2
3
4
5
6<!-- Log4j 2 API 和 SLF4J 之间的 Apache Log4j 绑定。 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-to-slf4j</artifactId>
<version>2.23.1</version>
</dependency>