理解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. gRPC 和传统 RPC 有啥不一样?一篇讲清楚!

    现在大家做系统开发,都喜欢搞"微服务架构"--简单说就是把一个大系统拆成很多小服务,这样更灵活也更容易扩展.那这些服务之间怎么沟通呢?就得靠一种技术叫 RPC(远程过程调用).今天 ...

  2. 如何在 Java 中进行内存泄漏分析?

    如何在 Java 中进行内存泄漏分析? 内存泄漏是指程序中无法访问的对象仍然被占用内存,导致内存无法回收,最终导致内存不足.程序崩溃等问题.Java 中的内存泄漏通常与垃圾回收机制的工作方式相关,虽然 ...

  3. 揭秘AI编排爆火真相:从"人工智障"到"真正智能"的关键一跃

    当行业还在追捧大模型参数竞赛时,领先团队早已转向新战场: AI编排(Agent Orchestration)-- 这个方向是 AI 技术"从聊天到做事"的关键突破口. 1.为什么说 ...

  4. 6.4K star!轻松搞定专业领域大模型推理,这个知识增强框架绝了!

    嗨,大家好,我是小华同学,关注我们获得"最新.最全.最优质"开源项目和高效工作学习方法 「垂直领域大模型落地难?逻辑推理总出错?这个来自OpenSPG的开源框架,让专业领域知识服务 ...

  5. 106套Axure RP大数据可视化大屏模板及通用组件库

    106套Axure RP大数据可视化大屏模板包括了多种实用美观的可视化组件库及行业模板库,行业模板涵盖:金融.教育.医疗.政府.交通.制造等多个行业提供设计参考. 随着大数据的发展,可视化大屏在各行各 ...

  6. Excel 拼接为 SQL 并打包 exe

    关于 Excel 拼接 sql 这个操作, 我已经整过好几篇了, 当然在工作中也是蛮常用的, 今天主要是来写个终篇, 彻底结束它, 然后将代码进行打包为 exe 这样的桌面小软件, 除了自己用, 也可 ...

  7. ISCC2025破阵夺旗赛三阶段Misc详解 By Alexander

    ISCC2025破阵夺旗赛三阶段Misc详解 By Alexander 写在前面:十八天吃石终于结束了,第一次就让我见到了这个比赛有多么的构式,平台是构式的,睡一觉就1000解了,全是对flag的渴望 ...

  8. 2、Java语言基础知识

    数据类型及范围 四种:整型(byte,short,int,long).浮点型(float,double).字符型(char).布尔型(true,false) 类型 byte short int lon ...

  9. Win32汇编学习笔记09.SEH和反调试

    Win32汇编学习笔记09.SEH和反调试-C/C++基础-断点社区-专业的老牌游戏安全技术交流社区 - BpSend.net SEH - structed exception handler 结构化 ...

  10. 基于vue3项目开发+MonacoEditor实现外部引入依赖,界面化所见即所得

    最近一个项目中,基于vue3开发,想开发一个在线管理组件库的功能,具体业务实现: 1. 在私库Nexus上传组件包: 2. 然后用UNPKG实现路径访问在线解压文件: 3. 解压文件上传到gitee组 ...