一、日志的目的
日志,通常不会在需求阶段作为一个功能单独提出来,也不会在产品方案中看到它的细节.但是,这丝毫不影响它在任何一个系统中的重要的地位.为了保证服务的高可用,发现问题一定要及时,解决问题一定要迅速,所以生产环境一旦出现问题,预警系统就会通过邮件、短信甚至电话的方式实施多维轰炸模式,确保相关负责人不错过每一个可能的 bug.预警系统判断疑似 bug 大部分源于日志.比如某个微服务接口由于各种原因导致频繁调用出错,此时调用端会捕获这样的异常并打印 ERROR 级别的日志,当该错误日志达到一定次数出现的时候,就会触发报警.
二、常用的日志门面和日志实现
1、日志门面
JCL(Jakarta Commons Logging): Apache Jakarta 小组开发的日志门面
jboss-logging: 特定的框架使用的日志门面,一般我们用不到,例如 Hibernate 框架使用的日志接口就是 jboss-logging
SLF4J: Simple Logging Facade For Java
2、日志实现
Log4j2: Apache 重新推出的日志框架
JUL: java.util.logging 的日志实现
Log4j 和 Logback 是同一个人开发的,由于 Log4j 存在性能问题,然后 Log4j 日志框架的开发人员想要对 Log4j 进行升级,然后就有了 Logback 这个日志框架,而为了能够使日志系统具有扩展性,才写了 SLF4J 这个抽象层的日志门面.
当然还有很多没有列举出来的日志框架,为什么有了 Commons Logging 和 Log4j ,又会蹦出来 SLF4J 和 Logback?这是因为 Java 有着非常悠久的开源历史,不但 OpenJDK 本身是开源的,而且我们用到的第三方库,几乎全部都是开源的.开源生态丰富的一个特定就是,同一个功能,可以找到若干种互相竞争的开源库.因为对 Commons Logging的接口不满意,有人就搞了 SLF4J.因为对 Log4j 的性能不满意,有人就搞了 Logback.
从目前的趋势来看,越来越多的开源项目从 Commons Logging + Log4j 转向了 SLF4J + Logback 的日志架构
而我们今天的主角 Springboot 使用的日志架构就是 SLF4J + Logback
三、怎么使用 SLF4J 日志框架
我们可以查看 SLF4J 的官网去查看如何使用
前面我们讲到了日志门面(日志接口)和日志实现框架,这里的 SLF4J 就是一个日志接口,我们使用具体的 API 的时候调用的是日志接口中的方法,它会根据实际引入的日志实现类框架去调用具体的日志实现方法.所以呢,我们需要根据具体的日志实现框架引入对应的配置文件
SLF4J unbound: 只需要导入一个 slf4j-api.jar 就可以
SLF4J bound to logback-classic: 导入 SLF4J 日志接口的 jar 包 slf4j-api.jar、Logback 日志实现的 jar 包 logback-classic.jar、logback-core.jar
SLF4J bound to log4j: 前面我们提到过, logback 的出现就是为了对 log4j 的升级,为了扩展性,才推出了 SLF4J 这个日志接口,所以 SLF4J 这个日志接口出现的时间是要比 Log4j 日志实现框架要晚的,这个时候你想使用 SLF4J 日志门面,而具体的日志实现框架使用 Log4j ,那么就必须需要一个中间转换包,这里对应的就是 Adaptation layer(适配层:它具体的原理是:slf4j-log412.jar 包中实现 slf4j-api.jar 中的所有方法,而方法中调用的是 log4j 的日志功能方法去进行日志记录)
其它的就不进行说明了...
理论上是比较简单,但是在实际情况下,往往比上述描述的情况复杂一些,例如我们想使用 SLF4J + Logback 的方式来进行日志记录,可是我们项目中往往会引入其它的依赖,例如Spring 它使用的日志接口是 Commons-Logging,如果没有这个日志接口, Spring 启动就会报错,那么我们怎么使项目统一使用 SLF4J + Logback 的方式进行日志记录呢?
进入官网: http://www.slf4j.org/
点击 leagcy APIs 之后我们可以看到统一的日志处理图示,由于我们这里是想使用 SLF4J + Logback 的日志方式,所以我们就讲这一种
可以看到这里面有很多的日志,有 Spirng 的 Commons logging 日志、有 log4j 的日志、有 java.util.logging 日志,这里面的做法就是通过引入对应的中间转换 jar包(红色圈住的那几个 jar 包)
jcl-over-slf4j.jar: 这个 jar 包的作用就是既包含 Spring 的 Commons logging 日志的所有内容,这样 Spring 启动的时候就不会因为缺少 Commons logging 而报错了,又包含具体的日志输出方式,也就是采用 SLF4J 的方式输出日志
log4j-ocer-slf4j.jar: 和上面的 jcl-over-slf4j.jar 作用类似, Log4j 转换为 SLF4J 的中间转换包
jul-to-slf4j.jar: java.util.logging 转换为 SLF4J 的中间转换包
所以我们想要统一项目中的日志具体的实现步骤如下:
1、将系统中的其它日志框架排除出去(因为日志的中间替换包中已经有了该日志的实现,如果不排除出去的话就会存在日志实现的 jar 包冲突问题)
2、使用中间转换包替换原有的日志框架,防止其它依赖中有使用原有的日志框架而报错
3、导入 SLF4J 的其它实现(我们这里是 SLF4J + Logback)
四、Springboot 的日志关系
Springboot 中是通过 spring-boot-starter-logging 来完成相关的日志配置的
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
我们从下面的依赖图中也可以看出来,项目中 jcl-over-slf4j(java commons logging 转换成 slf4j 的中间转换包)、log4j-over-slf4j(log4j 转换成 slf4j 的中间转换包)、jul-to-slf4j(java.util.logging 转换成 slf4j 的中间转换包),而项目中真正使用的是 logback 的日志实现框架.
Springboot 日志常用的配置
在 application.yaml 中添加相应配置
一、指定文件的名称/路径
1、只指定文件的路径( path 带盘符)
logging:
path: E:/springLog
最终会在 E:springLog 路径下生成一个默认名称的文件来保存日志,文件名称是 spring.log
2、指定路径和文件名称( path 带日志文件名称带盘符)
logging:
path: E:/springLog/springboot.log
这种情况下, springboot 会将 springboot.log 当成是一个目录,而不会当成是一个日志文件,最终会在 E:/springLog/springboot.log 目录下面生成一个默认的spring.log 文件
3、指定路径( path 不带盘符)
logging:
path: /springLog/
最终会在当前项目所在盘符的根目录下生成一个 springLog 目录,并且在 springLog 目录下面会生成一个默认名称的 spring.log 文件来保存日志
4、只指定文件名称( file 不带盘符)
logging:
file: springboot.log
最终会在当前项目的根路径下生成一个 springboot.log 用来保存日志
5、同时指定路径和文件名称( file 带路径和盘符)
logging:
file: E:/springLog/springboot.log
最终会在 E:/springLog/ 目录下生成一个 springboot.log 用来保存日志
6、同时指定路径和文件名称(不带盘符)
logging:
file: /springboot/springboot.log
最终会在该项目所在盘符的根目录下生成一个 springboot 目录,在 springboot 目录下面生成一个 springboot.log 用来保存日志
7、同时包含 path 和 file
logging:
path: /spring/
file: springboot.log
这个时候同时有 path 和 file 选项,它们是冲突的, springboot 默认会选择 file 的配置项来进行处理,也就是会在当前项目下面生成一个 springboot.log 用来保存日志信息
二、指定日志输出格式 logging.pattern (日志输出格式)
logging:
file: springbootLog.log
pattern:
# 在控制台输出的日志的格式
console: '%d{yyyy-MM-dd}----> [%thread] %-5level----> %logger{50}----> %msg%n'
# 在指定文件中日志输出的格式
file: '%d{yyyy-MM-dd} === [%thread] === %-5level === %logger{50} === %msg%n'
%d :表示日期时间
%thread :表示线程名
%‐5level :级别从左显示 5 个字符宽度
%logger{50} :表示 logger 名字最长 50 个字符,否则按照句点分割.
%msg :日志消息
%n :换行符
三、指定日志输出级别
logging:
level:
# 指定日志的级别,这里指的是 com.xiaomaomao 包下面的日志级别是 trace ,其它的依旧使用 springboot 默认的 info
com.xiaomaomao: trace
四、指定配置(自定义配置)
进入 Springboot 官网---->找到你项目中的 Springboot 对应的版本---->Reference Document
找到日志相关的章节---->Custom Log Configuration(这里面有私人订制日志的相关介绍)
给类路径下放上每个日志框架自己的配置文件即可,由于 Springboot 使用的日志实现是 Logback,我们可以找到 Logback 日志实现对应的配置文件
关于 logback.xml 和 logback-spring.xml 配置文件的区别,查询了一些博客,以及培训班的视频,大意如下:
logback.xml:直接就被日志框架识别了,这个时候你就不能使用 Springboot 的 profile 功能
spring:
profiles:
active: sit
IDEA 参数配置(--spring.profiles.active=sit)
五、切换日志框架
我们知道 Springboot 默认使用的是 SLF4J + Logback 的方式来记录日志的,如果我们想切换为其它的日志形式,那么该怎么做呢?
1、切换 SLF4J + Log4j
进入官网:http://www.slf4j.org/---->点击 leagcy APIs---->找到 SLF4J + log4j 的图示
再看一下整个项目的依赖关系图
找到 pom.xml---->show dependences---->找到相关的日志依赖模块
通过上面两张图可以得出具体的步骤:
1、剔除 Springboot 使用的 Logback 的日志依赖
2、剔除项目中的 jcl、jul 日志依赖,并且引入对应的中间转换包
3、引入 slf4j-log412.jar 这个依赖,并且一定要剔除 log4j-over-slf4j 这个中间转换包,否则会 jar 包冲突
在 IDEA 依赖图中选中要排除的依赖---->exclude---->便可以排除了
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> // 排除 logback 的日志依赖 <exclusions> <exclusion> <artifactId>logback-classic</artifactId> <groupId>ch.qos.logback</groupId> </exclusion> // 排除 log4j 的中间转换包防止 jar 包冲突 <exclusion> <artifactId>log4j-over-slf4j</artifactId> <groupId>org.slf4j</groupId> </exclusion> </exclusions> </dependency> // 引入 slf4j-log4j12 的依赖 <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.7</version> </dependency>
2、切换 log4j2 的日志形式
通过这张图我们可以发现,这里有两个场景启动器,一个是我们默认使用的 spring-boot-starter-logging,如果你想使用 log4j2 的日志形式,只需要排除 spring-boot-starter-logging 这个依赖,然后再引入 spring-boot-starter-log4j2 的依赖就可以.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring‐boot‐starter‐web</artifactId>
// 排除 spring‐boot‐starter‐logging
<exclusions>
<exclusion>
<artifactId>spring‐boot‐starter‐logging</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
// 引入 spring‐boot‐starter‐log4j2
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring‐boot‐starter‐log4j2</artifactId>
</dependency>