当前位置 : 首页 » 文章分类 :  开发  »  Spring-Log

Spring-Log

Spring Logging 笔记


Spring Boot Logging

Spring Boot Logging
https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.logging

使用 Spring Boot 的 Starters 时,默认使用 Logback 日志框架。


默认控制台日志

即便不加任何日志配置,Spring Boot 也会有默认的控制台日志 Appender CONSOLE 输出,但没有日志文件输出。

默认的控制台日志配置在 spring-boot-3.1.6.jar 包中 org/springframework/boot/logging/logback/console-appender.xml,内容:

<included>
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>${CONSOLE_LOG_THRESHOLD}</level>
        </filter>
        <encoder>
            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
            <charset>${CONSOLE_LOG_CHARSET}</charset>
        </encoder>
    </appender>
</included>

如果不想再写一个控制台 appender,可以直接 include 引入 Spring Boot 自带的这个默认 CONSOLE appender 配置:

<configuration debug="true">
  <include resource="org/springframework/boot/logging/logback/console-appender.xml"/>
  <root>
    <appender-ref ref="CONSOLE"/>
  </root>
</configuration>

默认控制台日志格式:

2023-11-23T13:39:52.622Z  INFO 35705 --- [myapp] [           main] o.s.b.d.f.logexample.MyApplication       : Starting MyApplication using Java 17.0.9 with PID 35705 (/opt/apps/myapp.jar started by myuser in /opt/apps/)
2023-11-23T13:39:52.628Z  INFO 35705 --- [myapp] [           main] o.s.b.d.f.logexample.MyApplication       : No active profile set, falling back to 1 default profile: "default"
2023-11-23T13:39:53.900Z  INFO 35705 --- [myapp] [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port 8080 (http)
2023-11-23T13:39:53.923Z  INFO 35705 --- [myapp] [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]

在 application.yml 中配置日志级别

从 Spring Boot v1.2.0.RELEASE 开始可以在 application.yml/properties 中配置日志级别,这些配置项是独立于任何日志框架实现的

通过 logging.level.<logger-name>=<level> 配置具体类包的日志级别,level 是 TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF 之一

logging:
  level:
    root: INFO
    feign.Logger: DEBUG
  file:
    path: /root/logs/myapp

Spring Boot: How can I set the logging level with application.properties?
https://stackoverflow.com/questions/20485059/spring-boot-how-can-i-set-the-logging-level-with-application-properties


定制日志配置

使用不同的日志框架时,Spring Boot 自动加载的日志配置:

  • Logback: logback-spring.xml, logback-spring.groovy, logback.xml, or logback.groovy
  • Log4j2: log4j2-spring.xml or log4j2.xml
  • JDK (Java Util Logging): logging.properties

Spring Boot 建议使用带 -spring 的日志配置文件,例如 logback-spring.xml 代替 logback.xml,方便应用 Spring Boot 定制的日志扩展


application.yml 中的日志配置参数

SpringBoot application.properties 或 application.yml 中可配置日志相关配置项,这些配置项是独立于任何日志框架实现的
在 application.yml/properties 配置后,可在 logback-spring.xml 中直接使用对应的变量,常用配置对应关系如下:

Spring Boot 2.x 和 3.x 中都有的配置:
${LOG_FILE} 对应 SpringBoot 中配置项 logging.file.name 日志文件
${LOG_PATH} 对应 SpringBoot 中配置项 logging.file.path 日志目录
${FILE_LOG_PATTERN} 对应 SpringBoot 中配置项 logging.pattern.file 文件类型的 appender 中使用的日志输出格式

Spring Boot 2.x 中有的配置,3.x 中删除了:
${ROLLING_FILE_NAME_PATTERN} 对应 SpringBoot 中配置项 logging.pattern.rolling-file-name

Spring Boot 3.x 中新增的配置,2.x 中没有:
${LOG_EXCEPTION_CONVERSION_WORD} 对应 SpringBoot 中配置项 logging.exception-conversion-word

完整的配置在对应 Spring Boot 版本的源码 org.springframework.boot.logging.LoggingSystemProperties 文件中

Spring Boot 2.x 中的可用日志配置参数:
https://docs.spring.io/spring-boot/docs/2.2.0.RELEASE/api/org/springframework/boot/logging/LoggingSystemProperties.html

全部参数见 4.8. Custom Log Configuration
https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.logging.custom-log-configuration


Spring-Logging 配置 logback

https://docs.spring.io/spring-boot/docs/current/reference/html/howto.html#howto.logging.logback


logback.xml 和 logback-spring.xml 区别

logback.xml 是 logback 标准配置文件,加载的时机较早,在其中无法使用 spring 定制化配置。
logback-spring.xml 是 Spring Boot 扩展后的 logback 配置,在其中可使用 Spring 定制化配置。

SpringBoot Logback 扩展
https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.logging.logback-extensions


产生 LOG_PATH_IS_UNDEFINED 目录问题

原因:
logback 先于 Spring 初始化,导致初始化时找不到 spring 的 ${LOG_PATH} 变量,logback 不支持懒加载。

解决:
使用 logback-spring.xml 文件

https://stackoverflow.com/questions/25251983/springboot-with-logback-creating-log-path-is-undefined-folder


Spring Boot + Logback 配置最佳实践

说明:
1、日志目录配置:
local 和 prod 环境日志目录不同,在 application.yml 中使用 logging.file.path 分 profile 配置,本地启动时使用 logs 相对目录,prod 环境容器启动时使用容器内的固定目录,避免出现 prod 环境的目录本地不存在,本地启动报错的问题。
logback-spring.xml 中使用 ${LOG_PATH} 变量配置日志路径,读取 application.yml/properties 中的 logging.file.path 配置项。

2、日志文件名配置:
local 和 prod 环境的日志文件名是相同的,不需要根据环境改变配置,在 application.yml 中配置默认 logging.file.name${spring.application.name},不管哪个服务都适用。
logback-spring.xml 中使用 ${LOG_PATH}/${LOG_FILE} 拼接在一起作为日志文件名,重新定义了一个变量 LOG_PATH_FILE <property name="LOG_PATH_FILE" value="${LOG_PATH}/${LOG_FILE}"/>,用于当前日志/错误文件和滚动日志/错误文件。
建议 logging.file.name 不带 .log 扩展名,当前日志/错误文件和滚动日志/错误文件都可以用。

3、日志级别配置:
<root> 中不需要再配置 level 属性,完全由 application.yml/properties 中的 logging.level.root 来配置日志级别即可

配置示例:
1、application.yml

spring:
  profiles:
    active: local
  application:
    name: blog

logging:
  file:
    name: ${spring.application.name}
  level:
    root: info

---
spring:
  config:
    activate:
      on-profile: local

logging:
  file:
    path: logs

---
spring:
  config:
    activate:
      on-profile: prod

logging:
  file:
    path: /root/logs/${spring.application.name}

2、logback-spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">
    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
    <!-- 完整日志文件名,读取 application.yml/properties 中的 logging.file.path 和 logging.file.name 配置项拼接在一起 -->
    <property name="LOG_PATH_FILE" value="${LOG_PATH}/${LOG_FILE}"/>
    <property name="PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %green([%thread]) %highlight(%-5level) %cyan(%logger).%M:%L - %msg%n" />

    <!-- 控制台输出,命名为 STDOUT 而不是 CONSOLE 是为了避免和 Spring Boot 自带的 org/springframework/boot/logging/logback/console-appender.xml 中的 CONSOLE appender 重名-->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>${logging.level}</level>
        </filter>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>
    </appender>

    <!-- 日志文件-->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <encoder>
            <pattern>${PATTERN}</pattern>
        </encoder>
        <file>${LOG_PATH_FILE}.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <cleanHistoryOnStart>${LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START:-false}</cleanHistoryOnStart>
            <fileNamePattern>${LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN:-${LOG_PATH_FILE}.%d{yyyy-MM-dd}.%i.log}</fileNamePattern>
            <maxFileSize>${LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE:-100MB}</maxFileSize>
            <maxHistory>${LOGBACK_ROLLINGPOLICY_MAX_HISTORY:-30}</maxHistory>
            <totalSizeCap>${LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP:-2GB}</totalSizeCap>
        </rollingPolicy>
    </appender>

    <!-- 仅 error 日志文件-->
    <appender name="FILE-ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <encoder>
            <pattern>${PATTERN}</pattern>
        </encoder>
        <file>${LOG_PATH_FILE}-error.log</file>
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>error</level>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <cleanHistoryOnStart>${LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START:-false}</cleanHistoryOnStart>
            <fileNamePattern>${LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN:-${LOG_PATH_FILE}-error.%d{yyyy-MM-dd}.%i.log}</fileNamePattern>
            <maxFileSize>${LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE:-100MB}</maxFileSize>
            <maxHistory>${LOGBACK_ROLLINGPOLICY_MAX_HISTORY:-30}</maxHistory>
            <totalSizeCap>${LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP:-2GB}</totalSizeCap>
        </rollingPolicy>
    </appender>

    <!-- root 中不需要再配置 level 属性,完全由 application.yml/properties 中的 logging.level.root 来配置日志级别即可 -->
    <root>
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="FILE"/>
        <appender-ref ref="FILE-ERROR"/>
    </root>

</configuration>

SpringBoot 改用 Log4j2 日志

spring boot 支持的日志框架有,logback,Log4j2,Log4j 和 Java Util Logging

Spring Boot 默认使用 Logback 日志框架

添加maven依赖

编辑pom.xml文件,排除logback的依赖包,添加log4j2的依赖包,如果要使用异步日志还需要添加 disruptor 依赖

<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    <exclusions>
      <!-- 排除logback -->
      <exclusion>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-logging</artifactId>
      </exclusion>
    </exclusions>
  </dependency>

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

  <!-- log4j2 异步支持 -->
  <dependency>
    <groupId>com.lmax</groupId>
    <artifactId>disruptor</artifactId>
  </dependency>
</dependencies>

注意要从 spring-boot-starter 依赖中排除 spring-boot-starter-logging, 只从 spring-boot-starter-web 中排除是不行的。

日志配置log4j2.xml

在资源文件夹 src/main/resources 下添加 log4j2.xml 或者 log4j2-spring.xml,启动后spring boot会自动识别并加载,此外,Spring Boot 还会识别 log4j2.jsonlog4j2.yaml

<?xml version="1.0" encoding="UTF-8"?>
<!-- configuration
  status用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出。可以设置成OFF(关闭) 或 Error(只输出错误信息)。
  TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF
  monitorInterval, 30s刷新此配置
-->
<configuration status="WARN" monitorInterval="30">

  <properties>
    <property name="PATTERN">%d{yyyy-MM-dd HH:mm:ss,SSS} [%-5level] [%thread] %c [%L]: %msg%n</property>
    <Property name="LOG_DIR">/data/log/blog</Property>
  </properties>

  <appenders>
    <Console name="Console" target="system_out">
      <PatternLayout pattern="${PATTERN}"/>
    </Console>

    <RollingRandomAccessFile name="RollingRandomAccessFile"
      fileName="${LOG_DIR}/blog.log"
      filePattern="${LOG_DIR}/blog-%d{yyyy-MM-dd}.log">
      <PatternLayout pattern="${PATTERN}"/>
      <Policies>
        <TimeBasedTriggeringPolicy interval="1"/>
      </Policies>
      <DefaultRolloverStrategy>
        <Delete basePath="${LOG_DIR}" maxDepth="1">
          <IfFileName glob="blog-*.log"/>
          <IfLastModified age="3d"/>
        </Delete>
      </DefaultRolloverStrategy>
    </RollingRandomAccessFile>

  </appenders>

  <loggers>
    <AsyncRoot level="info" includeLocation="true">
      <appenderref ref="Console"/>
      <appenderref ref="RollingRandomAccessFile"/>
    </AsyncRoot>
  </loggers>

</configuration>

log4j2不产生日志文件

sprig boot 配置 log4j2 后不产生日志文件,一开始以为是没有配置异步日志的依赖 disruptor 后来配上发现还是不行,折腾了好几天,最后仔细看了下sprig启动时报的 slf4j 警告,发现启动时有两个 slf4j StaticLoggerBinder 的实现类

SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/Users/si.ma/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/Users/si.ma/.m2/repository/org/apache/logging/log4j/log4j-slf4j-impl/2.10.0/log4j-slf4j-impl-2.10.0.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [ch.qos.logback.classic.util.ContextSelectorStaticBinder]

如上我项目 classpath 中同时有 logback 和 log4j 的 StaticLoggerBinder.class 类实现,slf4j最终自动选择了logback日志实现,感觉可能是这个原因导致log4j日志文件没有创建。

配置maven依赖时已经从 spring-boot-starter-web 中排除了 spring-boot-starter-logging ,但通过查看 maven 依赖树,发现还是有 logback-classic 依赖
通过 mvn dependency:tree 命令查看依赖树,发现确实还有 logback 依赖存在,如下:

[INFO] com.masikkk:blog:jar:0.0.1-SNAPSHOT
[INFO] +- org.springframework.boot:spring-boot-starter:jar:2.0.4.RELEASE:compile
[INFO] |  +- org.springframework.boot:spring-boot:jar:2.0.4.RELEASE:compile
[INFO] |  |  \- org.springframework:spring-context:jar:5.0.8.RELEASE:compile
[INFO] |  +- org.springframework.boot:spring-boot-autoconfigure:jar:2.0.4.RELEASE:compile
[INFO] |  +- org.springframework.boot:spring-boot-starter-logging:jar:2.0.4.RELEASE:compile
[INFO] |  |  +- ch.qos.logback:logback-classic:jar:1.2.3:compile
[INFO] |  |  |  \- ch.qos.logback:logback-core:jar:1.2.3:compile

原来 spring-boot-starter 依赖了 spring-boot-starter-logging 依赖了 logback-classic ,将其从依赖中排除,再次启动spring,成功生成log文件,日志所在的目录都会自动创建的。

<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>

SLF4J: Class path contains multiple SLF4J bindings
https://stackoverflow.com/questions/14024756/slf4j-class-path-contains-multiple-slf4j-bindings


上一篇 Spring-Cloud

下一篇 2019年运动记录

阅读
评论
3.1k
阅读预计15分钟
创建日期 2019-02-25
修改日期 2024-01-20
类别

页面信息

location:
protocol:
host:
hostname:
origin:
pathname:
href:
document:
referrer:
navigator:
platform:
userAgent:

评论