Spring 日志

本文最后更新于:2024年9月8日 晚上

Spring 日志

Slf4j

  • Slf4j是一个日志框架,它是对所有日志框架制定的一种规范,标准,接口,并不是一个框架的具体的实现,因为接口并不能独立使用,它需要和具体的日志框架来配合使用。
  • 在Java中具体的日志实现有Log4j,Log4j2,Slf4j,JDKLog,Logback等等。

Logback

  • Spring Boot默认使用的日志框架。

日志级别

  • 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL
    • Trace:是追踪,程序每推进一步就可以写个trace输出,所以trace应该会很多。
    • Debug:指出细粒度信息事件对调试应用程序是非常有帮助的。
    • Info:消息在粗粒度级别上突出强调应用程序的运行过程。
    • Warn:输出警告及warn以下级别的日志。
    • Error:输出错误信息日志。
    • Fatal:输出每个严重的错误事件将会导致应用程序的退出的日志。
  • 程序会打印高于或等于所设置级别的日志,设置的日志等级越高,打印出来的日志就越少

配置

1
2
3
4
5
6
7
8
logging:
level:
root: info
com.example.mapper: debug
file: my.log
pattern:
console: d{yyyy/MM/dd-HH:mm:ss} [%thread] %-5level %logger-%msg%n
file: d{yyyy/MM/dd-HH:mm:ss} [%thread] %-5level %logger-%msg%n
  • logging.level:日志级别。

  • logging.file:设置文件,可以是绝对路径,也可以是相对路径,如:logging.file=my.log,默认情况下,日志文件的大小达到10MB时会切分一次,产生新的日志文件,默认级别为:ERROR,WARN,INFO

    • logging.file.max-size:最大日志文件大小。
    • logging.file.max-history:最大归档文件数量。
  • logging.path:设置目录,会在该目录下创建spring.log文件,并写入日志内容,如:logging.path=/var/log

  • logging.pattern:设置日志格式。

    • logging.pattern.console:控制台输出的日志模式。
    • logging.pattern.file:文件输出的日志模式。
    • logging.pattern.dateformat:日志的日期格式。

配置文件

  • Spring Boot 中的 logback 默认使用 src/main/resources 文件夹下的 logback.xmllogback-spring.xml ,也可以通过logging.config指定配置文件。
  • application.properities
1
2
3
# 日志配置信息
logging:
config: classpath:logback-spring.xml
  • logback-spring.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<property name="app_name" value="tally"/>
<property name="log_home" value="logs/${app_name}"/>
<property name="log_file_upload_count" value="3"/>
<property name="log_file_count" value="15"/>
<property name="log_size" value="20MB"/>
<property name="upload_log_size" value="5MB"/>
<property name="patternLayout"
value="[%X{traceID}]$$%d{yyyy-MM-dd HH:mm:ss.SSS}$$%level{}$$[${HOSTNAME}]$$[%class]$$[%line]$$[%method]$$[%thread]$$%msg%n"/>
<property name="patternLayoutUpload" value="%date{yyyy-MM-dd HH:mm:ss} ** %m%n"/>
<property name="encoding" value="UTF-8"/>

<!--控制台输出-->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${patternLayout}</pattern>
<charset>${encoding}</charset>
</encoder>
</appender>

<!--noSpecify日志-->
<appender name="noSpecify" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log_home}/noSpecify.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>
${log_home}/%d{yyyy-MM-dd}/noSpecify-%d{yyyyMMdd}-%i.log.gz
</fileNamePattern>
<maxHistory>${log_file_count}</maxHistory>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>${log_size}</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
<pattern>${patternLayout}</pattern>
<charset>${encoding}</charset>
</encoder>
</appender>

<!--业务日志-->
<appender name="business" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log_home}/business.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>
${log_home}/%d{yyyy-MM-dd}/business-%d{yyyyMMdd}-%i.log.gz
</fileNamePattern>
<maxHistory>${log_file_count}</maxHistory>
<maxFileSize>${log_size}</maxFileSize>
</rollingPolicy>
<encoder>
<pattern>${patternLayout}</pattern>
<charset>${encoding}</charset>
</encoder>
</appender>

<!--数据库执行日志-->
<appender name="sql" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log_home}/sql.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>
${log_home}/%d{yyyy-MM-dd}/sql-%d{yyyyMMdd}-%i.log.gz
</fileNamePattern>
<maxHistory>${log_file_count}</maxHistory>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>${log_size}</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
<pattern>${patternLayout}</pattern>
<charset>${encoding}</charset>
</encoder>
</appender>

<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="noSpecify"/>
</root>

<logger name="com.lushan.tally.mapper" level="DEBUG">
<appender-ref ref="sql"/>
</logger>

<logger name="com.lushan.tally.service" level="INFO">
<appender-ref ref="business"/>
</logger>

<!-- 排除无用日志-->
<!-- <logger name="org.springframework.integration" level="ERROR"/>-->
<!-- <logger name="org.mybatis.spring.SqlSessionUtils" level="ERROR"/>-->

</configuration>

  • Pattern
    • %d 表示时间,用 %d{yyyy-MM-dd HH:mm:ss.SSS} 定义了格式化时间,也可以使用 %date
    • %-5level 表示显示日志级别,并且用 5 个字符靠左对齐,也可以用 %p 表示日志级别。
    • %thread 表示显示日志进程名字。
    • %logger{80} 表示日志输出者的名字最长80个字符,否则按照句点分割,常常是类名。
    • %msg 日志消息。
    • %n 平台的换行符。
    • %c 用来在日志上输出类的全名。
    • %L 表示行号。
  • charset 设置日志编码格式为 UTF-8,避免中文乱码。
  • root 标签内设置日志级别 level,等同于在配置文件中设置 logging.pattern.level
  • Appender:Appender 是一个日志打印的组件,定义了打印过滤的条件,打印输出方式,滚动策略,编码方式,打印格式等等,但是它仅仅是一个打印组件,如果不使用一个 logger 或者 root 的 appender-ref 指定某个具体的 appender 时,它就没有什么意义。
    • ConsoleAppender :需要设置这样的一个 apppender,否则日志不会打印到控制台。
    • RollingFileAppender:滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件。
      • rollingPolicy 设置滚动切分的规则。
        • fileNamePattern:滚动时产生的文件的存放位置及文件名称。
          • %d{yyyy-MM-dd}:按天进行日志滚动。
          • %i:当文件大小超过maxFileSize时,按照i进行文件滚动。
        • totalSizeCap 用来指定日志文件的上限大小,这个设置在 maxFileSize 之后起效,也就是说,如果你文件上限设置的是 1MB,但是 maxFileSize 设置的是 10MB,那么,这个日志文件也会保存为 10MB,假设你 maxFileSize 设置的是 1MB,totalSizeCap 设置的是 2MB,那么你日志文件的个数最多也就 2 个。
        • MaxHistory 可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件,假设设置每天滚动,且maxHistory是365,则只保存最近365天的文件,我还是喜欢这个设置,比 totalSizeCap 意义更清晰。
        • maxFileSize:当日志文件超过maxFileSize指定的大小是,根据上面提到的%i进行日志文件滚动。
  • Root与Logger:可以包含零个或多个<appender-ref>元素,标识这个 appender 将会添加到这个 logger
    • level:日志打印级别,如果未设置此属性,那么当前 logger 会继承上级的级别,也就是 root 的级别。
    • name:用来指定受此 logger 约束的某一个包或者具体的某一个类,root没有该属性,默认是所有没有被其他logger约束的类。
    • addtivity:是否向上级 logger 传递打印信息,默认是 true,设置为 false 表示该日志打印设置(控制台打印还是文件打印等具体设置)不会向根 root 标签传递,也就是说该 logger 里怎么设置的那就会怎么打印,跟 root 无关。

Log4j2

日志级别

  • 日志level共有8个级别,按照从低到高为:All < Trace < Debug < Info < Warn < Error < Fatal < OFF
    • All:最低等级的,用于打开所有日志记录。
    • Trace:是追踪,程序每推进一步就可以写个trace输出,所以trace应该会很多。
    • Debug:指出细粒度信息事件对调试应用程序是非常有帮助的。
    • Info:消息在粗粒度级别上突出强调应用程序的运行过程。
    • Warn:输出警告及warn以下级别的日志。
    • Error:输出错误信息日志。
    • Fatal:输出每个严重的错误事件将会导致应用程序的退出的日志。
    • OFF:最高等级的,用于关闭所有日志记录。
  • 程序会打印高于或等于所设置级别的日志,设置的日志等级越高,打印出来的日志就越少

pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!-- Exclude Spring Boot's Default Logging -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>

<!-- Add Log4j2 Dependency -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>

<!-- Needed for Async Logging with Log4j 2 -->
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.3.6</version>
</dependency>

配置文件

  • src/main/resources中添加log4j2.xml配置文件(Spring Boot 还会识别log4j2.jsonlog4j2.yaml)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<?xml version="1.0" encoding="UTF-8"?>
<configuration status="WARN" monitorInterval="30">
<!--先定义所有的appender-->
<appenders>
<!--这个输出控制台的配置-->
<console name="Console" target="SYSTEM_OUT">
<!--输出日志的格式-->
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
</console>
<!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,这个也挺有用的,适合临时测试用-->
<File name="log" fileName="log/test.log" append="false">
<PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/>
</File>
<!-- 这个会打印出所有的info及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
<RollingFile name="RollingFileInfo" fileName="${sys:user.home}/logs/info.log"
filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/info-%d{yyyy-MM-dd}-%i.log">
<!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
<ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="100 MB"/>
</Policies>
</RollingFile>
<RollingFile name="RollingFileWarn" fileName="${sys:user.home}/logs/warn.log"
filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/warn-%d{yyyy-MM-dd}-%i.log">
<ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="100 MB"/>
</Policies>
<!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件,这里设置了20 -->
<DefaultRolloverStrategy max="20"/>
</RollingFile>
<RollingFile name="RollingFileError" fileName="${sys:user.home}/logs/error.log"
filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/error-%d{yyyy-MM-dd}-%i.log">
<ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="100 MB"/>
</Policies>
</RollingFile>
</appenders>
<!--然后定义logger,只有定义了logger并引入的appender,appender才会生效-->
<loggers>
<!--过滤掉spring和mybatis的一些无用的DEBUG信息-->
<logger name="org.springframework" level="INFO"></logger>
<logger name="org.mybatis" level="INFO"></logger>
<root level="all">
<appender-ref ref="Console"/>
<appender-ref ref="RollingFileInfo"/>
<appender-ref ref="RollingFileWarn"/>
<appender-ref ref="RollingFileError"/>
</root>
</loggers>
</configuration>
  • 根节点Configuration有两个属性:status和monitorinterval,有两个子节点:Appenders和Loggers(表明可以定义多个Appender和Logger)
    • status用来指定log4j本身的打印日志的级别。
    • monitorinterval用于指定log4j自动重新配置的监测间隔时间,单位是s,最小是5s
  • Appenders节点,常见的有三种子节点:Console,RollingFile,File
    • Console节点用来定义输出到控制台的Appender
      • name:指定Appender的名字。
      • target:SYSTEM_OUT 或 SYSTEM_ERR,一般只设置默认:SYSTEM_OUT
      • PatternLayout:输出格式,不设置默认为:%m%n
    • File节点用来定义输出到指定位置的文件的Appender
      • name:指定Appender的名字。
      • fileName:指定输出日志的目的文件带全路径的文件名。
      • PatternLayout:输出格式,不设置默认为:%m%n
    • RollingFile节点用来定义超过指定大小自动删除旧的创建新的的Appender
      • name:指定Appender的名字。
      • fileName:指定输出日志的目的文件带全路径的文件名。
      • PatternLayout:输出格式,不设置默认为:%m%n
      • filePattern:指定新建日志文件的名称格式。
      • Policies:指定滚动日志的策略,就是什么时候进行新建日志文件输出日志。
      • TimeBasedTriggeringPolicy:Policies子节点,基于时间的滚动策略,interval属性用来指定多久滚动一次,默认是1 hour,modulate=true用来调整时间:比如现在是早上3am,interval是4,那么第一次滚动是在4am,接着是8am,12am…而不是7am
      • SizeBasedTriggeringPolicy:Policies子节点,基于指定文件大小的滚动策略,size属性用来定义每个日志文件的大小。
      • DefaultRolloverStrategy:用来指定同一个文件夹下最多有几个日志文件时开始删除最旧的,创建新的(通过max属性)
  • Loggers节点,常见的有两种:Root和Logger
    • Root节点用来指定项目的根日志,如果没有单独指定Logger,那么就会默认使用该Root日志输出。
      • level:日志输出级别,共有8个级别,按照从低到高为:All < Trace < Debug < Info < Warn < Error < Fatal < OFF
      • AppenderRef:Root的子节点,用来指定该日志输出到哪个Appender
    • Logger节点用来单独指定日志的形式,比如要为指定包下的class指定不同的日志级别等。
      • level:日志输出级别,共有8个级别,按照从低到高为:All < Trace < Debug < Info < Warn < Error < Fatal < OFF
      • name:用来指定该Logger所适用的类或者类所在的包全路径,继承自Root节点。
      • AppenderRef:Logger的子节点,用来指定该日志输出到哪个Appender,如果没有指定,就会默认继承自Root.如果指定了,那么会在指定的这个Appender和Root的Appender中都会输出,此时我们可以设置Logger的additivity="false"只在自定义的Appender中进行输出。

使用日志

1
2
3
4
5
6
7
8
9
10
11
public class LoggerTest {
private final Logger logger = LoggerFactory.getLogger(LoggerTest.class);

@Test
public void test(){
logger.debug("debug");
logger.info("info");
logger.error("error");
logger.warn("warn");
}
}
  • 使用Lombok,可以更方便的使用日志框架。
1
2
3
4
5
6
7
8
9
10
11
@Slf4j
public class LoggerTest {

@Test
public void test(){
log.debug("debug");
log.info("info");
log.error("error");
log.warn("warn");
}
}
  • 注意:在JVM启动参数中增加-DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector开启异步日志。

问题解决

项目不打印日志

报错:SLF4J: Class path contains multiple SLF4J bindings

原因:项目中既存在slf4j-log4j12的jar包又存在log4j-slf4j-impl的jar包,导致出现了两个打印日志实现类,所以slf4j门面不知道具体应该使用哪一个进行输出日志,所以导致不打印日志,之前是log4j 1.x,并且在web.xml里面指定了log4j.properties文件的位置,第一次运行项目,忘了修改其值为log4j2.xml,出现了Context [] startup failed due to previous errors的错误,导致项目启动不起来。

解决方法:修改web.xml

1
2
3
4
<context-param>
<param-name>log4jConfiguration</param-name>
<param-value>/WEB-INF/classes/log4j2.xml</param-value>
</context-param>

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!