日志文件是用于记录系统操作事件的集合,可分为事件日志和消息日志。日志文件主要记录系统行为的时间、地点、状态等相关信息,能够帮助我们了解并监控系统状态,在发生错误或者接近某种危险状态时能够及时提醒我们处理,同时在系统产生问题时,能够帮助我们快速的定位、诊断并解决问题。
2 Java常用日志框架JUL(java.util.logging)、JCL(Apache Commons Logging)、Logback、Log4j、Log4j2、Slf4j、Jboss-logging 等。
2.1 日志门面主要有:JCL、Slf4j、Log4j2、Jboss-logging等。日志门面即日志的一个抽象层,提供了使用各种日志实现的统一接口,它只是一个接口,并没有实现类。在项目中使用日志门面提供的接口输出日志,当我们更换日志框架的时候,不需要去修改项目代码,只需要配置使用哪种日志实现框架,项目运行时就会自动调用具体日志框架输出(面向接口编程)。日志门面最流行的是使用Slf4j。项目日志文件的内容格式、保存路径等配置,需要符合具体配置的日志框架语法,因为底层真正使用的是你配置的日志框架。
2.2 日志实现主要有:JUL、Logback、Log4j、Log4j2等。日志实现是能够直接单独使用的日志框架,但当项目更换不同的日志框架时,因为每种框架的使用的方法不同,因此需要修改项目中日志输出的代码。如果使用日志门面,就能够避免修改代码这个问题。
在项目中,一般选择一个日志门面(抽象层)+ 一个日志实现。
Spring 框架默认使用 JCL 日志框架,SpringBoot 使用:Slf4j 日志门面+ Logback 日志框架 。
3 Slf4j日志门面官网:https://www.slf4j.org/
目前市面上最流行的日志门面,现在的项目中,基本上都是使用Slf4j作为我们的日志系统。
3.1 Slf4j 日志绑定开发的时,日志方法的调用,不应该直接调用日志框架具体实现,而是调用日志门面(抽象层)里的方法,即面向接口编程。
日志绑定:Slf4j日志门面依赖 + 日志实现依赖。
日志绑定流程:
(1)添加 slf4j-api 的依赖
(2)绑定具体的日志实现框架
a 绑定已经实现了 slf4j的日志框架,直接添加对应的依赖
b 绑定没有实现 slf4j的日志框架,先添加该日志框架的适配器依赖,再添加日志实现的依赖
(3)项目中使用 slf4j的api进行统一的日志记录
(4)slf4j有且仅有一个日志实现框架的绑定,如果出现多个日志实现框架,默认使用排在第一个依赖的实现
项目中先导入 Slf4j 依赖,再导入日志实现的依赖,如果需要适配器的日志实现,只需导入适配器依赖即可,因为在适配器中已经导入了对应的日志实现依赖,当然也可以继续导入日志实现的依赖。Slf4j绑定日志实现配置图如下:
注意:每一个日志框架都有自己的配置文件。使用Slf4j以后,配置文件还是需要使用所选择的日志实现的配置文件。比如使用 Slf4j + Logback ,那么配置文件使用的配置需要符合 logback 配置要求。
3.2 Slf4j 的使用根据3.1 Slf4j日志绑定图,添加绑定依赖如下,添加依赖后,idea工具可在pom.xml文件中鼠标右键点击Diagrams选项,查看依赖结构图表,展开结构图表后,你会发现有的日志实现其实不用导入 Slf4j日志门面的依赖,如:reload4j日志实现,在适配器已经导入了Slf4j的api依赖,当然也可以导入Slf4j日志门面的依赖,不会有影响。
org.slf4j slf4j-api 1.7.36 org.slf4j slf4j-reload4j 1.7.36 junit junit 4.12
测试类:
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Slf4jTest {
private static Logger log = LoggerFactory.getLogger(Slf4jTest.class);
@Test
public void test() {
//使用slf4j日志门面的api,底层会自动调用具体日志实现
log.trace("这是 trace 日志。。。");
log.debug("这是 debug 日志。。。");
log.info("这是 info 日志。。。");
log.warn("这是 warn 日志。。。");
log.error("这是 error 日志。。。");
}
}
reload4j日志实现需要的配置 log4j.properties 文件。
log4j.rootLogger=debug, stdout, R log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%5p - %m%n log4j.appender.R=org.apache.log4j.RollingFileAppender log4j.appender.R.File=firestorm.log log4j.appender.R.MaxFileSize=100KB log4j.appender.R.MaxBackupIndex=1 log4j.appender.R.layout=org.apache.log4j.PatternLayout log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n log4j.logger.com.codefutures=DEBUG3.3 桥接器/统一日志框架
不同的框架,往往都有自己默认的日志框架,如:Spring(JCL),Hibernate(jboss-logging)、MyBatis等等。统一日志框架,就是将别的日志框架,也一起统一使用slf4j进行日志输出。桥接器能够解决项目中日志的遗留问题,比如以前项目中使用log4j做日志记录,现在需要切换使用logback日志框架,没有桥接器以前需要去修改项目代码,有了slf4j桥接器后,可以通过桥接器直接切换到slf4j日志门面,再使用logback日志实现输出,并不需要去修改项目代码。统一日志框架也是一样,先将框架默认的日志框架排除,再使用桥接器,最后就可以做到统一日志输出。
桥接器的使用步骤:
(1)先去除之前的日志框架的依赖
(2)添加对应的slf4j提供的桥接器依赖(其实就是将桥接器替换掉原有的日志框架jar包)
(3)添加slf4j的具体日志实现
a 绑定已经实现了 slf4j的日志框架,直接添加对应的依赖
b 绑定没有实现 slf4j的日志框架,先添加该日志框架的适配器依赖,再添加日志实现的依赖
桥接器使用示意图:
桥接器的原理:
其实就是偷天换日,创建一个跟框架默认使用的日志框架一样的jar包(桥接器),然后将框架默认使用的日志jar去掉,桥接器跟原来的日志框架包路径相同,具有同样方法,只是在这些方法是空的,并在这些方法中做一层跳转,跳转到slf4j日志门面,当各框架调用自己日志方法输出的时候,最终就会调用slf4j输出。
org.slf4j slf4j-api 1.7.36 org.slf4j log4j-over-slf4j 1.7.36 ch.qos.logback logback-classic 1.2.11 junit junit 4.12
测试类:使用桥接器后,测试类中的代码都不需要修改(包路径不需要修改,日志输出方法也不需要修改),经过桥接器跳转后,最终会调用logback输出。
//这里使用的是log4j的对象(包路径)
import org.apache.log4j.Logger;
import org.junit.Test;
public class Slf4jBridgingTest {
// 定义log4j日志对象
private static final Logger log4j = Logger.getLogger(Slf4jBridgingTest.class);
// 测试桥接器
@Test
public void test() {
log4j.trace("这是 trace 日志。。。");
log4j.debug("这是 debug 日志。。。");
log4j.info("这是 info 日志。。。");
log4j.warn("这是 warn 日志。。。");
log4j.error("这是 error 日志。。。");
}
}
使用log4j输出日志:
DEBUG - 这是 debug 日志。。。 INFO - 这是 info 日志。。。 WARN - 这是 warn 日志。。。 ERROR - 这是 error 日志。。。
桥接后,日志门面为logback输出日志:
00:48:31.671 [main] DEBUG top.mengh.Slf4jBridgingTest - 这是 debug 日志。。。 00:48:31.681 [main] INFO top.mengh.Slf4jBridgingTest - 这是 info 日志。。。 00:48:31.681 [main] WARN top.mengh.Slf4jBridgingTest - 这是 warn 日志。。。 00:48:31.681 [main] ERROR top.mengh.Slf4jBridgingTest - 这是 error 日志。。。4 logback 日志实现的使用
官网:https://logback.qos.ch/
logback是由log4j创始人设计的另外一个开源日志组件,性能比log4j要好。
logback主要分为三个模块:
logback-core:其他两个模块的基础模块
logback-classic:是log4j的一个改良版本,同时完整实现了slf4j的api
logback-access:访问模块与servlet容器集成,提供通过http来访问日志的功能
4.1 logback配置文件logback启动时,会依次读取以下类型的配置文件,如果均不采用,会采用默认配置。
logback.groovy
logback-test.xml
logback.xml
logback组件之间的关系:
Logger:日志对象,记录器。把它关联到对应的context上后,主要用于存放日志对象,也可以定义日志类型、级别。
Appender:用于指定日志输出的目的地,目的地可以时控制台、文件、数据库等。
Layout:负责把事件转换成字符串,格式化的日志信息输出。在logback中,Layout对象被封装在encoder中。
logback.xml文件基本配置信息:
5 log4j2的使用System.err ${pattern} ${log_dir}/logback.log ${pattern} ERROR ACCEPT DENY ${log_dir}/logback.html ${htmlPattern} ${log_dir}/roll_logback.log ${pattern} ${log_dir}/%d{yyyy-MM-dd}/rolling.%d{yyyy-MM-dd-HH}.%i.log.gz 1MB
log4j2参考了logback的一些优秀设计,并修复了一些问题,因此带来了一些重大的提升,主要有:
-
异常处理:在logback中,appender中的异常不会被应用感知到,但是在log4j2中,提供了一些异常处理机制;
-
性能提升:log4j2相较于log4j和logback都具有很明显的性能提升;
-
自动重载配置:参考了logback的设计,提供自动刷新参数配置,最实用的就是我们在生产上可以动态修改日志的级别而不需要重启应用;
-
无垃圾机制:log4j2在大部分情况下,都可以使用其设计一套无垃圾机制,避免频繁的日志收集导致的jvm gc。
log4j2的性能主要体现在:异步日志,和无垃圾机制。
目前市面上最主流的日志门面就是slf4j,虽然log4j2也是日志门面,但大部分人习惯了slf4j,所以一般还是将log4j2看做日志实现,slf4j + log4j2搭配使用。
添加依赖
org.apache.logging.log4j
log4j-api
2.17.2
org.apache.logging.log4j
log4j-core
2.17.2
java代码
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.junit.Test;
public class Log4j2Test {
private static final Logger log = LogManager.getLogger(Log4j2Test.class);
@Test
public void test() {
log.fatal("这是 fatal 日志。。。");
log.trace("这是 trace 日志。。。");
log.debug("这是 debug 日志。。。");
log.info("这是 info 日志。。。");
log.warn("这是 warn 日志。。。");
log.error("这是 error 日志。。。");
}
}
5.2 使用slf4j作为日志门面
添加依赖
org.slf4j slf4j-api 1.7.36 org.apache.logging.log4j log4j-slf4j-impl 2.17.2 org.apache.logging.log4j log4j-api 2.17.2 org.apache.logging.log4j log4j-core 2.17.2 junit junit 4.12
java代码
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Slf4jTest {
private static final Logger log = LoggerFactory.getLogger(Slf4jTest.class);
@Test
public void test() {
log.trace("这是 trace 日志。。。");
log.debug("这是 debug 日志。。。");
log.info("这是 info 日志。。。");
log.warn("这是 warn 日志。。。");
log.error("这是 error 日志。。。");
}
}
5.3 log4j2配置
log4j2默认加载classpath下的 log4j2.xml文件中的配置。
5.4 log4j异步日志
log4j2的优越性能主要依靠log4j2的异步日志功能,log4j2提供了两种方式实现日志的方式,一个时通过AsyncAppender,一个是通过AsyncLogger,分别对应Appender组件和Logger组件。
配置log4j2异步日志需要添加依赖
com.lmax
disruptor
3.4.4
5.4.1 AsyncAppender 方式
这种方式使用不多,因为它的性能跟logback相差无几。
5.4.2 AsyncLogger 方式
AsyncLogger 是官方推荐的异步方式,它可以使得调用logger.log返回的更快,有两种选择,可以使用全局异步和混合异步。
全局异步:就是所有的日志都使用异步记录,在配置文件上不用做任何的改动,只需要添加一个log4j2.component.properties配置文件,并添加内容:
Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
混合异步(局部异步):就是你可以在应用中同时使用同步日志和异步日志,这使得日志的配置方式更加灵活。
使用异步日志需要注意的问题:
(1)如果使用异步日志,AsyncAppender 、AsyncLogger 和全局异步这三种方式不要同时出现,同时出现会造成所有日志性能跟最差的AsyncAppender 方式一样。
(2)设置includeLocation="false",打印行号位置信息会降低异步日志的性能,性能可能会比同步日志还差。
java代码
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Slf4jTest {
private static final Logger log = LoggerFactory.getLogger(Slf4jTest.class);
@Test
public void test() {
System.out.println("======start======");
System.out.println("======start======");
for (int i = 0; i < 10; i++) {
log.trace("这是 trace 日志。。。");
log.debug("这是 debug 日志。。。");
log.info("这是 info 日志。。。");
log.warn("这是 warn 日志。。。");
log.error("这是 error 日志。。。");
}
System.out.println("======end======");
System.out.println("======end======");
}
}
异步日志配置在控制台输出,会发现执行完了System.out.println()的代码,才会输出log日志。
======start====== ======start====== ======end====== ======end====== [00:07:13.172] [main] [DEBUG] top.mengh.Slf4jTest: --- 这是 debug 日志。。。 [00:07:13.172] [main] [INFO ] top.mengh.Slf4jTest: --- 这是 info 日志。。。 [00:07:13.172] [main] [WARN ] top.mengh.Slf4jTest: --- 这是 warn 日志。。。 [00:07:13.172] [main] [ERROR] top.mengh.Slf4jTest: --- 这是 error 日志。。。6 SpringBoot默认日志框架 6.1 SpringBoot 默认日志框架
SpringBoot底层默认使用的日志依赖:
org.springframework.boot spring-boot-starter-logging compile
底层依赖关系:
总结:
(1)SpringBoot 底层默认使用 Slf4j + Logback 方式进行日志记录;
(2)SpringBoot把其他的默认日志都替换成了slf4j;
SpringBoot使用日志最核心的总结:SpringBoot能自动适配所有的日志,而且底层使用slf4j+logback的方式记录日志,引入其他框架的时候,只需要把这个框架默认使用的日志框架排除掉。
SpringBoot使用日志的时候,只需要在类中获取Logger日志对象即可。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Demo{
private final Logger log = LoggerFactory.getLogger(Demo.class);
public void test(){
log.trace("这是 trace 日志。。。");
log.debug("这是 debug 日志。。。");
log.info("这是 info 日志。。。");
log.warn("这是 warn 日志。。。");
log.error("这是 error 日志。。。");
}
}
如果不想每次都使用LoggerFactory.getLogger()获取日志对象,则在 pom.xml文件中添加lombok依赖,再在使用的类上添加注解 @Slf4j 。添加lombok依赖,idea需要先安装lombok插件。
org.projectlombok lombok
在类的上方贴 @Slf4j 注解,即可使用,如:
@Slf4j
public class Demo{
public void test(){
log.info("这是 info 日志。。。");
}
}
日志等级优先级: trace < debug < info < warn < error
springBoot默认使用的级别是 info级别,所以只会输出含 info 以上级别的日志。
6.2 指定配置文件给类路径下放上每个日志框架自己的配置文件,那么springBoot就不会使用默认配置,在工程resource目录下放置。
| 日志实现 | 配置文件 |
|---|---|
| logback | logback-spring.xml,logback.xml |
| jul | logging.properties |
也可以使用全局配置文件,如application.yml 配置logback日志输出,application.yml全局配置不能和日志实现配置文件同时存在(如:logback.xml),否则会报错。
spring:
profiles:
active: dev
# slf4j日志配置
logging:
level:
# 日志等级优先级 trace < debug < info < warn < error
# 使用trace级别, 分包配置级别,不同的包下可以使用不同的级别
top.mengh: trace
file:
# name是在当前项目下生成名为 springboot.log 日志文件
#name: springboot.log
# path是在指定目录下生成日志文件,默认名为spring.log,如下面的路径为项目根目录/logs/spring.log
# 如果同时指定 name 和 path 时,只有 name 生效。
path: ./logs
pattern:
# 指定控制台日志输出格式
console: "[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %L [%thread] %m%n"
# 指定文件中日志格式
file: "[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %L [%thread] %m%n"
可以通过切换环境使用不同的日志格式化输出,如logback.xml:
6.3 日志实现切换 log4j2${pattern} [%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %L [%thread] ===== %m%n ${log_dir}/logback.log ${pattern}
添加依赖:先排除springboot默认的logback依赖,在添加log4j2的依赖。
org.springframework.boot spring-boot-starter-web 2.4.3 spring-boot-starter-logging org.springframework.boot org.springframework.boot spring-boot-starter-log4j2
这时候工程使用的配置文件得是log4j2的配置文件。
演示项目地址:https://gitee.com/meng_hui/slf4j



