1. JCL
JCL,Apache Commons Logging,它提供了统一的日志接口,实现了对其他日志工具的抽象,便于开发者在不同的日志工具间切换。
JCL支持的实现有Log4j,Avalon LogKit,以及JUL。
JCL有两个基本的抽象类,Log
(基础日志记录器)和LogFactory
(负责创建Log
实例)。
maven地址,
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
在没有其他日志实现类的支持下,JCL默认使用JUL实现,有如下代码,
Log log = LogFactory.getLog(JclTest.class);
log.info("hello jcl");
System.out.println(log);
console输出为,
十二月 22, 2022 1:31:51 上午 org.example.jcl.JclTest testQuick
org.apache.commons.logging.impl.Jdk14Logger@484b61fc
信息: hello jcl
pom文件添加log4j后,上述代码输出为,
log4j:WARN No appenders could be found for logger (org.example.jcl.JclTest).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
org.apache.commons.logging.impl.Log4JLogger@182decdb
2. SLF4J
JCL作为日志门面,仅支持log4j和JUL,对于未来新的日志框架没有很好的支持,SLF4J很好的解决了这一点。
SLF4J,Simple Logging Facade for Java,是目前市面上最流行的日志门面。它主要提供两大功能,
- 日志框架的绑定(Binding)
- 日志框架的桥接(Bridging)
SLF4J支持各种日志框架,对不同的日志框架,需要引入不同的绑定jar文件。
SLF4J使用流程如下,
- 添加
slf4j-api
的依赖 - 使用SLF4J的API进行统一的日志记录
- 绑定具体的日志实现框架
- 如logback和log4j2等,遵循了SLF4J规范,直接添加实现的依赖
- 对于log4j和JUL等,没有遵循SLF4J规范,需要先添加适配器依赖,再添加实现的依赖
- SLF4J有且仅有一个日志实现的绑定(如果有多个,默认使用第一个)
上述流程可以在SLF4J快速手册中查看。
对于原本使用log4j或JUL的项目,如何在不更改代码的情况下,改为使用SLF4J和logback?
SLF4J的桥接可以解决该问题,官方参考地址。
以log4j为例,首先将log4j依赖去除,接下来,引入log4j-over-slf4j
、slf4j-api
和logback-classic
依赖即可。此时,日志实现已经修改为logback。
注意:
不能将同一个实现的桥接依赖和适配器依赖同时引入,如log4j-over-slf4j
和slf4j-log4j
,否则,会发生死循环,引发栈内存溢出异常。
3. logback
logback分为三个模块,logback-core
,logback-classic
和logback-access
。其中,logback-access
可以与Servlet容器集成,自定义容器日志输出位置。
logback会依次读取以下配置文件,
- logback.groovy
- logback-test.xml
- logback.xml
如果都不存在,会采用默认配置。
logback也是由三个组件构成,Logger
,Appender
和Layout
。Layout
在logback被封装在encoder
中。
配置文件信息,
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<!--
配置属性的集中管理
可以直接修改属性的value值
使用方法类似EL表达式,如:${name}
-->
<property name="pattern" value="[%-5level]
%d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %L [%thread] %m%n" />
<property name="log_dir" value="logs" />
<!-- 控制台appender定义 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<!-- 控制输出流对象,默认System.out,改为System.err -->
<target>System.err</target>
<!-- layout配置 -->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${pattern}</pattern>
</encoder>
</appender>
<!-- 文件appender定义 -->
<appender name="file" class="ch.qos.logback.core.FileAppender">
<file>${log_dir}/logback.log</file>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${pattern}</pattern>
</encoder>
</appender>
<!-- 日志拆分和归档压缩的appender定义 -->
<appender name="rollFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log_dir}/roll_logback.log</file>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${pattern}</pattern>
</encoder>
<!-- 指定拆分规则 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- 按照时间和压缩格式声明拆分的文件名 -->
<fileNamePattern>${log_dir}/rolling.%d{yyyy-MM-dd}.log%i.gz</fileNamePattern>
<!-- 按照文件大小拆分 -->
<maxFileSize>1MB</maxFileSize>
</rollingPolicy>
<!-- 过滤器 只记录error级别的信息 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 异步日志 -->
<appender name="async" class="ch.qos.logback.classic.AsyncAppender">
<!-- 指定某个具体的appender -->
<appender-ref ref="rollFile" />
</appender>
<!-- root logger配置 -->
<root level="ALL">
<appender-ref ref="console" />
<appender-ref ref="async" />
</root>
</configuration>