理解SpringBoot 的日志设计

在项目中导入spring-boot-starter.jar依赖,它会传递 导入spring-boot-starter-logging.jar依赖,依赖关系如下图:

spring-boot-starter-logging.jar依赖三个jar包:

  1. logback-classic.jar:它传递依赖于logback-core.jar和slf4j-api.jar
  2. log4j-to-slf4j.jar:它传递依赖于log4j-api.jar和slf4j-api.jar
  3. jul-to-slf4j.jar:它传递依赖于slf4j-api.jar

java的日志框架比较多,常见的包括:SLF4J、Log4j、Log4j2、Logback、common-logging(JCL)、java.util.logging(JUL)、JBoss Logging等,这些日志框架又分:

  1. 门面类(抽象层):SLF4J、JCL、JBoss Logging
  2. 日志实现:Log4j、Log4j2、Logback、common-logging(JCL)

SpringBoot默认使用SLF4J+Logback组合,SLF4J作为日志门面(应用程序输出日志时应该面向改API),Logback作为日志实现。

由于SpringBoot要整合大量的第三方框架,这些框架可能使用JCL、Log4j、JUL等。因此SpringBoot提供对应的日志路由,将其他框架生成的日志信息统一路由给SLF4J处理。从上面依赖关系可以看出:

  1. log4j-to-slf4j.jar:负责将Log4j日志路由到SLF4J
  2. jul-to-slf4j.jar:负责将JUL日志路由到SLF4J

虽然SpringBoot默认采用Logback作为底层日志实现,但通过配置允许将底层日志实现改为其他框架。SpringBoot允许将Logback依赖排除出去,添加其他日志实现(比如log4j)的依赖。

需要注意的是:当吧SpringBoot应用部署到Web服务器或应用服务器上时,JUL生成的日志不会被路由到SpringBoot应用的日志中,这是为了避免将服务器或者服务器上的其他应用的日志也路由到SpringBoot的日志中,否则会造成日志混乱。

日志级别与格式

代码示例:控制器类

@RestController
public class HelloController
{
Logger logger = LoggerFactory.getLogger(this.getClass());
@GetMapping
public Map<String, Object> hello()
{
logger.trace("-------TRACE级别的日志-------");
logger.debug("-------DEBUG级别的日志-------");
logger.info("-------INFO级别的日志-------");
logger.warn("-------WARN级别的日志-------");
logger.error("-------ERROR级别的日志-------");
return Map.of("hello", "Hello");
}
}

日志级别主要分为(级别由低到高):

  1. all:输出所有日志
  2. trace
  3. debug
  4. info
  5. warn
  6. error
  7. fatal:log4j增加的一种日志级别,代表“致命错误”,比error级别更高。(由于SpringBoot不支持此级别,因此会被自动转换为error级别)
  8. off:关闭所有日志

日志系统有一个规则:当日志的输出方法的级别高于或等于日志的设置级别,该日志才会实际输出。

比如日志级别设为info,当程序使用info()、warn()、error()输出时,日志才会实际输出;使用trace()、debug()输出的日志会被忽略。

因此,日志级别越高,输出日志就越精简,性能开销越小;日志级别越低,输出日志就越详细,性能开销越大。一般项目处于开发、测试、试运行阶段,日志级别设置的低;在项目实际运行阶段,日志级别设置的高。

由上图可知,SpringBoot默认的日志级别是Info。

SpringBoot输出的日志包括如下信息:

  1. 日期和时间:精确到毫秒
  2. 日志级别
  3. 进程ID
  4. 分隔符:三个减号(---)
  5. 线程名:方括号里面的内容(在控制台输出时可能会被截断)
  6. 日志名:通常是完整类名(为了便于阅读,包名经常简写)
  7. 日志信息

设置日志级别的几种方式:

  1. 通过debug=true或trace=true等属性(可通过配置文件、命令行参数、系统变量、OS环境变量等方式)改变整个SpringBoot核心的日志级别
  2. 通过logging.level.=属性(可通过配置文件、命令行参数、系统变量等方式)设置日志级别。其中代表日志名,通常是包名或全限定类名,而Level可以是各种日志级别。

需要注意的是,当启用trace或debug模式时,SpringBoot的核心日志(包括嵌入式容器、Hibernate和SpringBoot)被设为对应的级别,但是其他程序组件不会被设为对应级别。

例如:添加命令行参数: --trace 。 启动上面代码运行结果如下:

可见,程序组件本身的日志级别没有改变。

那么要设置程序组件的日志级别,要通过logging.level.=属性来设置

logging:
level:
# 将org.crazyit.app包及其子包下所有日志级别设为TRACE
org.crazyit.app: trace

运行结果如下

学会之后解决以下几个问题就很简单了。

  1. 让Mybatis输出SQL语句(logging.level.<mapper组件所在的包>=debug)
  2. 输出Redis的详细执行过程(logging.level.io.lettuce.core=debug)
  3. 输出MongoDB的详细执行过程(logging.level.com.mongodb=debug)

SpringBoot允许通过spring.output.ansi.enabled属性设置是否用不同颜色来区分不同级别日志,该属性支持以下属性值:

  1. always:总是启用
  2. detect:自动检查。如果控制台支持ansi颜色特性,则启用。这是默认值。
  3. never:不启用

如果要改变控制台的日志格式,可通过logging.pattern.console属性进行设置。其默认值是:

%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint}

%clr(${LOG_LEVEL_PATTERN:-%5p})

%clr(${PID:- }){magenta}

%clr(---){faint}

%clr([%15.15t]){faint}

%clr(%-40.40logger{39}){cyan}

%clr({faint}

%m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}

上面配置由多个“%clr(输出内容){颜色值} ”片段组成,每个片段代表一个输出元素,其中{颜色值} 用于指定该片段的颜色。此处颜色值支持如下几个值:

  1. blue:蓝色
  2. cyan:青色
  3. faint:原色
  4. green:绿色
  5. magenta:紫红色
  6. red:红色
  7. yellow:黄色

如果不指定颜色之后,直接使用“%clr(输出内容)”,表面使用默认的颜色。

比如上面设置包含以下片段:

  1. %clr(${LOG_LEVEL_PATTERN:-%5p}) :表明以日志级别对应的颜色来输出
  2. %clr(${PID:- }){magenta} :以紫红色输出进程ID
  3. %clr(---){faint} :以原色来输出三个减号(---)

假如输出日志不要显示日期、时间,设置时去掉“%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint} ”片段即可。

需要注意的是:logging.pattern.console属性仅当使用Logback日志实现时才有效。

输出日志到文件

要将日志输出到文件,设置如下两个属性之一:

  1. logging.file:设置日志文件
  2. logging.path:设置日志文件目录。使用默认的spring.log作为文件名。

使用logging.file或logging.path属性输出日志到文件

logging.file.name logging.file.path 示例 描述
只输出到控制台
指定文件 my.log 输出到特定文件,文件路径可以是绝对或相对路径
指定目录 /f:/log 写入指定路径下的spring.log文件,该路径可以是绝对或相对路径

SpringBoot默认只将info、warn、error三个级别的日志输出到文件。

当日志文件达到10MB时,会自动使用新文件。若要改变这个设置,对于Logback日志实现(SpringBoot默认),可直接使用application.properties(或application.yml)设置;对于其他日志实现,需要对应的日志设置文件来设置。比如log4j,需要使用log4j.xml设置。

Logback的日志设置

属性名称 描述
logging.logback.rollingpolicy.file-name-pattern 设置对日志归档的文件名模版
logging.logback.rollingpolicy.clean-history-on-start 应用启动时是否清除日志文档
logging.logback.rollingpolicy.max-file-size 日志文件归档之前的最大大小
logging.logback.rollingpolicy.total-size-cap 日志归档被删除之前所能容纳的最大大小
logging.logback.rollingpolicy.max-history 设置保留多少天的日志归档(默认7天)

代码示例:控制器类org.crazyit.app.controller

@RestController
@Slf4j
public class HelloController
{
@GetMapping
public Map<String, Object> hello()
{
log.trace("-------TRACE级别的日志-------");
log.debug("-------DEBUG级别的日志-------");
log.info("-------INFO级别的日志-------");
log.warn("-------WARN级别的日志-------");
log.error("-------ERROR级别的日志-------");
return Map.of("hello", "Hello");
}
}

上面的控制器类使用@Slf4j注解修饰,里面的方法可直接使用 log.trace()等方法来输出日志,那这个log对象是从哪来的?

因为本例使用Lombok工具,这个工具专门通过各种注解来生成常用的代码,比如以下常用的注解:

  1. @Getter:为所有实例变量生成getter方法
  2. @Setter:为所有非final实例变量生成setter方法
  3. @ToString:自动生成toString()方法
  4. @EqualsAndHashCode:自动生成equals()和hashCode()方法
  5. @AllArgsConstructor:自动生成带所有参数的构造器
  6. @NoArgsConstructor:自动生成无参构造器
  7. @Data:自动生成一个数据类,相当于@Getter、@Setter、@ToString、@EqualsAndHashCode、@AllArgsConstructor、@NoArgsConstructor等注解的组合
  8. @Log、@Log4j、@Log4j2、@Slf4j、@CommonsLog、@JBossLog、@Flogger:为对应的日志实现生成一个日志对象

为了在应用中使用Lombok,需要做一下两件事:

  1. 添加Lombok依赖:

    1. <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.16</version>
      </dependency>
  2. 为IDEA添加Lombok插件,通过主菜单FIle-》Settings-》Plugins-》搜索插件,然后安装即可。

代码示例:application-addition.yml

logging:
level:
# 将org.crazyit.app包及其子包下所有日志级别设为TRACE
org.crazyit.app: trace
file:
# 指定日志文件的输出目录,在应用的根目录下生成logs文件夹,该文件夹下生成默认文件名为spring.log
# path: logs/
# 指定日志文件,生成在应用的根目录下
name: my.log

日志组

日志组就是将多个包、类组合在一起,起一个名字,以后可直接对改组设置日志级别,对改组设置就相当于同时为改组内的所有包及其子包、类统一设置了日志级别。

代码示例:控制器类,为上面的控制器类再增加这个控制器类org.fkjava.app.controller

@RestController
@Slf4j
public class FkController
{
@GetMapping("/fk")
public Map<String, Object> hello()
{
log.trace("-------TRACE级别的日志-------");
log.debug("-------DEBUG级别的日志-------");
log.info("-------INFO级别的日志-------");
log.warn("-------WARN级别的日志-------");
log.error("-------ERROR级别的日志-------");
return Map.of("hello", "Hello");
}
}

代码示例:application-addition.yml

logging:
group:
# 将org.crazyit.app和org.fkjava.app两个包定义成fkapp组
fkapp: org.crazyit.app, org.fkjava.app
level:
# 将fkapp组对应的包及其子包的所有日志级别设为TRACE
fkapp: trace

关闭控制台日志

如果想改变SpringBoot的底层日志实现(放弃Logback),则需要如下2步:

  1. 去掉Logback依赖库,添加新日志实现的依赖库
  2. 在类加载路径的根路径下为新日志提供对应的配置文件

SpringBoot默认从类加载路径的根路径下加载日志框架的配置文件,也可通过logging.config属性来设置新的加载路径。

SpringBoot既可以根据底层依赖库自动选择合适的日志实现,也可通过org.springframework.boot.logging.LoggingSystem属性显式指定日志实现。属性值可以是LoggingSystem实现类的全限定类名(比如Log4J2LoggingSystem, LobbackLoggingSystem, JavaLoggingSystem等类的全限定类名);也可设置为none,也就彻底关闭SpringBoot的日志系统了。

注意:由于日志初始化会在ApplicantContext创建之前完成,因此不能通过SpringBoot的配置文件来配置logging.config、org.springframework.boot.logging.LoggingSystem等日志控制属性,只能通过系统属性来设置。

不同日志系统对应的配置文件

日志系统 配置文件
Logback logback-spring.xml, logback-spring.groovy, logback.xml或logback.groovy
Log4j2 log4j2-spring.xml或log4j2.xml
JDK(JUL) logging,properties

注意:SpringBoot推荐使用带-spring的配置文件,比如Logback日志系统,使用 logback-spring.xml更好。此外尽量避免使用JUL日志系统,因此JUL的类加载机制会导致一些问题。

对日志进行定制的属性

SpringBoot属性 系统属性 说明
logging.exception-conversion-word LOG_EXCEPTION_CONVERSION_WORD 记录异常的转换字
logging.file.name LOG_FILE 指定日志文件名
logging.file.path LOG_PATH 指定日志输出路径,使用spring.log作为文件名
logging.pattern.console CONSOLE_LOG_PATTERN 控制台日志的格式模板
logging.pattern.dateformat LOG_DATEFORMAT_PATTERN 日期格式模板
loggging.charset.console CONSOLE_LOG_CHARSET 输出控制台日志的字符集
logging.pattern.file FILE_LOG_PATTERN 文件日志的格式模板,仅当日志输出到文件时才有效
logging.charset.file FILE_LOG_CHARSET 文件日志所用的字符集,仅当日志输出到文件时才有效
logging.pattern.level LOG_LEVEL_PATTERN 指定输出日志级别时使用的格式(默认为%5p)
PID PID 当前进程ID

SpringBoot为logback提供了一些通用的配置文件,开发者只要导入这些配置文件,即可使用预定义的配置。这些文件位于org/springframework/boot/logging/logback/路径下,其中常用的有:

  1. defaults.xml:提供了转换规则及各种通用配置
  2. console-appender.xml:定义一个ConsoleAppender,将日志输出到控制台
  3. file-appender.xml:定义一个RollingFileAppender,将日志输出到文件

代码示例:logback-spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 导入Logback通用的日志配置 -->
<include resource="org/springframework/boot/logging/logback/defaults.xml" />
<!-- 定义日志文件 -->
<property name="LOG_FILE" value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}/}spring.log}"/>
<!-- 导入输入到文件的日志配置 -->
<include resource="org/springframework/boot/logging/logback/file-appender.xml" />
<!-- 指定将日志输出到文件 -->
<root level="INFO">
<appender-ref ref="FILE" />
</root>
</configuration>

在上面的配置文件中可指定如下占位符:

  1. ${PID}:当前进程ID
  2. ${LOG_FILE}:代表是否通过外部配置设置了logging.file.name属性
  3. ${LOG_PATH}:代表是否通过外部配置设置了logging.file.path属性
  4. ${LOG_EXCEPTION_CONVERSION_WORD}:代表是否通过外部配置设置了logging.exception-conversion-word属性

上面配置将日志输出到文件,因此必须在application-addition.yml指定logging.file.name或logging.file.path属性。

代码示例:application-addition.yml

logging:
level:
# 将org.crazyit.app包及其子包下所有日志级别设为TRACE
org.crazyit.app: trace
file:
# 指定日志文件的输出目录,默认文件名为spring.log
# path: logs/
# 指定日志文件
name: my.log

此例子不会在控制台输出日志,仅在当前目录的my.log文件中输出日志。

改用Log4j2日志实现

去除Logback依赖库,添加Log4j2依赖库

<!-- Spring Web依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<!-- 去除spring-boot-starter-logging日志 -->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>

通过上面配置,项目底层日志框架改成了Log4j2,得益于SpringBoot的日志的抽象机制,上层程序使用的日志没有任何改变。

如果要对Log4j2自定义配置,可通过log4j2.yml或log4j2.json配置。

Logback扩展

可通过logback-spring.xml对Logback配置扩展功能。注意:不能使用logback.xml,因为该文件的加载时机太早,SpringBoot的其他基础功能还没来得及加载。

springProfile标签的name属性可指定相关Profile的日志配置

代码示例:logback-spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 以下配置仅当活动Profile为default、dev和test时有效 -->
<springProfile name="default | dev | test">
<include resource="org/springframework/boot/logging/logback/defaults.xml" />
<property name="LOG_FILE" value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}}/spring.log}"/>
<include resource="org/springframework/boot/logging/logback/console-appender.xml" />
<include resource="org/springframework/boot/logging/logback/file-appender.xml" />
<root level="DEBUG">
<appender-ref ref="CONSOLE" />
<appender-ref ref="FILE" />
</root>
<!-- 指定org.crazyit.app日志的级别是DEBUG -->
<logger name="org.crazyit.app" level="DEBUG"/>
</springProfile>
<!-- 以下配置仅当活动Profile为prod时有效 -->
<springProfile name="prod">
<include resource="org/springframework/boot/logging/logback/defaults.xml" />
<property name="LOG_FILE" value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}}/spring.log}"/>
<include resource="org/springframework/boot/logging/logback/console-appender.xml" />
<include resource="org/springframework/boot/logging/logback/file-appender.xml" />
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="FILE" />
</root>
<!-- 指定org.crazyit.app日志的级别是INFO -->
<logger name="org.crazyit.app" level="INFO"/>
</springProfile>
</configuration>

此外,logback的配置文件还可读取SpringBoot的配置属性,通过<springProperty.../> 元素来获取,该元素支持以下属性:

  1. name:为读取到的属性值指定名字
  2. source:指定读取哪个配置属性。推荐使用“烤串”写法(比如application-dev)
  3. scope:指定存储该配置属性的作用域
  4. defaultValue:当配置属性不存在时,指定默认值

比如:

<springProperty scope="context" name="fluentHost" source="myapp.fluentd.host" defaultValue="localhost" />
<appender name="FLUENT" class="ch.qos.logback.more.appenders.DataFluentAppender">
<!-- 使用前面定义的fluentHost属性-->
<remoteHost>${fluentHost}</remoteHost>
</appender>

SpringBoot--学会配置日志的更多相关文章

  1. springboot(三)配置日志

    github代码:https://github.com/showkawa/springBoot_2017/tree/master/spb-demo/spb-brian-query-serviceSLF ...

  2. springBoot项目配置日志打印管理(log4j2)

    1.修改pom文件引用log4j2相关jar包 依赖代码: <!-- log4j2 start --><!-- Spring Boot log4j2依赖 --><depe ...

  3. SpringBoot系列之日志框架使用教程

    目录 1.SpringBoot日志级别 1).日志级别简介 2).默认日志级别 3).配置日志级别 4).日志分组设置 2.SpringBoot日志格式设置 1).默认格式原理简介 2).默认日志格式 ...

  4. SpringBoot Logback配置,SpringBoot日志配置

    SpringBoot Logback配置,SpringBoot日志配置  SpringBoot springProfile属性配置 ================================ © ...

  5. SpringBoot优雅地配置日志

    本文主要给大家介绍SpringBoot中如何通过sl4j日志组件优雅地记录日志.其实,我们入门 JAVA 的第一行代码就是一行日志,那你现在还在使用System.out.println("H ...

  6. springboot通过slf4j配置日志

    原因:SpringBoot默认使用slf4j日志,引入其他框架的时候,只需要把这个框架依赖的日志框架排除掉: 而我今天想引入log4j的时候,pom文件一直报错,显示找不到log4j的jar包,应当是 ...

  7. springboot项目配置logback日志系统

    记录springboot项目配置logback日志文件管理: logback依赖jar包 SpringBoot项目配置logback理论上需要添加logback-classic依赖jar包: < ...

  8. springboot中的日志配置

    日志方式:每天日志存放在一个文件中,info和warn日志存放一个文件,error存放一个文件 创建文件 logback-spring.xml <?xml version="1.0&q ...

  9. 仵航说 SpringBoot项目配置Log日志服务-仵老大

    今天领导让我配置一个log日志服务,我哪里见过哟,然后就去百度了,结果挨个试下去,找到了一个能用的,分享给大家 大致四个地方 分别是 1.pom文件需要引入依赖 2.创建一个TestLog类 3.在y ...

  10. 助力SpringBoot自动配置的条件注解ConditionalOnXXX分析--SpringBoot源码(三)

    注:该源码分析对应SpringBoot版本为2.1.0.RELEASE 1 前言 本篇接 如何分析SpringBoot源码模块及结构?--SpringBoot源码(二) 上一篇分析了SpringBoo ...

随机推荐

  1. 聊聊一体机与AI知识库

    提供AI咨询+AI项目陪跑服务,有需要回复1 之前写了一篇关于一体机的文章: DeepSeek一体机是个什么鬼 一体机产生的原因是春节期间DeepSeek的火爆带动了一些公司的AI需求,但很多公司如医 ...

  2. 揭秘 AI 工具的系统提示词「GitHub 热点速览」

    这次的五一假期,你打卡了哪些好玩的地方?️ 无论身在何处,都别忘了每周二来咱们的「GitHub 热点速览」打卡!准时为你奉上最新.最热的开源项目! 如果你也曾对 Cursor 这类 AI 编程工具的强 ...

  3. Java实现minio上传文件加解密操作

    一.背景与需求 在云存储场景中,数据安全是核心需求之一.MinIO作为高性能对象存储服务,支持通过客户端加密(CSE)在数据上传前完成加密,确保即使存储服务器被攻破,攻击者也无法获取明文数据.本文将详 ...

  4. 实现一个前端动态模块组件(Vite+原生JS)

    1. 引言 在前面的文章<使用Vite创建一个动态网页的前端项目>中我们实现了一个动态网页.不过这个动态网页的实用价值并不高,在真正实际的项目中我们希望的是能实现一个动态的模块组件.具体来 ...

  5. ActiveMQ的安装与部署

       ActiveMQ是Apache的一个开源项目,它是一个功能强劲的开源消息总线,也是一个中间件产品,它是JMS的一个实现.   在介绍ActiveMQ之前,先来复习一下J2EE中的JMS规范.JM ...

  6. 测试网络联接状况常用命令 ping 使用方法介绍

      了解和掌握下面几个命令将会有助于您更快地检测到网络故障所在,从而节省时间,提高效率.   ping是测试网络联接状况以及信息包发送和接收状况非常有用的工具,是网络测试最常用的命令.ping向目标主 ...

  7. Spring注解之@Autowired自动装配bean 综述

    @Autowired的工作原理是什么?在启动spring IoC时,容器自动装载了一个AutowiredAnnotationBeanPostProcessor后置处理器,当容器扫描到@Autowire ...

  8. PicGo使用简明教程及踩坑记录

    PicGo使用简明教程及踩坑记录 PicGo使用 我现在用的博客的记录方式是Typora+PicGo+阿里云oss,这一套配置好后就非常方便了,可以快捷上传图片到云服务器,并且阿里云的速度也是我试过的 ...

  9. 关于cc1链-lazymap版复现

    关于cc1链-lazymap版复现 思路,在cc链中最重要的其实是transform方法;其反射调用执行的性质+transformchain性质,导致可以通过构造反射调用链子,也就是Runtime.e ...

  10. 关于MUI框架混合AS开发app项目中遇到的百度地图闪退,不显示地图问题的一次记录

    才进入公司就让我解决MUI混合app出现的BUG,让只会纯纯原生的我有点崩溃,三天就要结果,不过幸不辱命,今天我把这个问题解决了. 这个BUG是:百度地图崩溃导致应用闪退 上图是H5+androidS ...