Spring Boot日志系统深度解析

在Spring Boot生态中,开发者可以自由选择Java Util Logging、Log4j2或Logback作为底层日志框架。当使用官方提供的Starter启动器构建项目时,Spring
Boot默认采用Logback作为日志实现方案。无论选择哪种日志框架,Spring Boot均提供了统一的配置方式,支持将日志内容输出到控制台或持久化到文件中。

实际上,spring-boot-starter基础启动器已自动集成了spring-boot-starter-logging,该模块通过SLF4J日志门面抽象层,将Logback作为默认的日志实现框架。

通过配置文件调整日志行为

Spring Boot允许在配置文件中直接定义日志参数,这种方式虽然简单但灵活性有限,以下为常用配置示例:

# 日志配置文件路径,例如使用Logback时可指定:classpath:logback.xml
logging.config=
# 异常日志转换格式
logging.exception-conversion-word=%wEx
# 日志文件名称,例如:application.log
logging.file=
# 包路径日志级别映射,例如:logging.level.org.springframework=DEBUG
logging.level.*=
# 日志文件存储目录,例如:/var/log
logging.path=
# 控制台输出格式(仅默认Logback配置有效)
logging.pattern.console=
# 文件输出格式(仅默认Logback配置有效)
logging.pattern.file=
# 日志级别显示格式(默认%5p,仅默认Logback配置有效)
logging.pattern.level=
# 是否注册日志系统关闭钩子
logging.register-shutdown-hook=false

实际应用配置示例:

# 全局日志级别设置为DEBUG
logging.level.root=DEBUG
# Spring Web模块日志级别为DEBUG
logging.level.org.springframework.web=DEBUG
# Hibernate框架日志级别设置为ERROR
logging.level.org.hibernate=ERROR

自定义日志配置文件进阶指南

不同日志框架会自动加载特定名称的配置文件,这些文件需放置在资源根目录下,其他目录或命名方式将无法被识别。

日志框架 可识别的配置文件名称
Logback logback-spring.xml, logback-spring.groovy, logback.xml, logback.groovy
Log4j2 log4j2-spring.xml, log4j2.xml
JDK(Java Util Logging) logging.properties

考虑到Logback作为Spring Boot的默认选择且功能强大,我们可以在资源目录下创建logback-spring.xml文件。以下是一个高度定制化的配置参考:

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">

    
    <springProperty scope="context" name="APP_NAME" source="spring.application.name"/>
    <springProperty scope="context" name="APP_PORT" source="server.port"/>
    <springProperty scope="context" name="DEFAULT_APP_PORT" source="spring.application.port"/>

    
    <property name="OS_NAME" value="${os.name}"/>
    <if condition='property("OS_NAME").contains("Windows")'>
        <then>
            <property name="LOG_PATH" value="${LOG_PATH:-E:/logs}"/>
        </then>
        <else>
            <property name="LOG_PATH" value="${LOG_PATH:-/log}"/>
        </else>
    </if>

    
    <property name="LOG_DIR" value="${APP_NAME:-system}"/>
    <property name="APP_PORT" value="${APP_PORT:-${DEFAULT_APP_PORT:-0}}"/>
    <if condition='!property("APP_PORT").equals("0")'>
        <then>
            <property name="LOG_DIR" value="${LOG_DIR}-${APP_PORT}"/>
        </then>
    </if>

    
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
    </appender>

    
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>INFO</level>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>${LOG_PATH}/${LOG_DIR}/info.log.%d{yyyy-MM-dd}.log</FileNamePattern>
            <MaxHistory>30</MaxHistory>
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
        <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
            <MaxFileSize>10MB</MaxFileSize>
        </triggeringPolicy>
    </appender>

    
    <appender name="FILE-ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>${LOG_PATH}/${LOG_DIR}/error.log.%d{yyyy-MM-dd}.log</FileNamePattern>
            <MaxHistory>30</MaxHistory>
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
        <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
            <MaxFileSize>10MB</MaxFileSize>
        </triggeringPolicy>
    </appender>

    
    <logger name="com.apache.ibatis" level="TRACE"/>
    <logger name="java.sql.Connection" level="DEBUG"/>
    <logger name="java.sql.Statement" level="DEBUG"/>
    <logger name="java.sql.PreparedStatement" level="DEBUG"/>

    
    <root level="INFO">
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="FILE"/>
        <appender-ref ref="FILE-ERROR"/>
    </root>

</configuration>

重要建议:优先使用logback-spring.xml作为配置文件名。因为标准的logback.xml加载时机过早,此时Spring的
ApplicationContext尚未创建,导致无法读取通过@PropertySources注解加载的属性。但以下类型的属性仍可正常访问:

  • 系统环境变量
  • Spring Environment中的属性
  • applicationbootstrap配置文件中的值

属性读取示例

  • 读取系统环境变量(带默认值):
    <property name="LOG_PATH" value="${LOG_PATH:-E:/logs}" />
  • 读取Spring Environment中的属性:
    <springProperty scope="context" name="fluentHost" source="myapp.fluentd.host" defaultValue="localhost"/>

环境感知配置:Spring Boot支持通过<springProfile>标签实现不同环境下的差异化配置:


<springProfile name="staging">
    
</springProfile>

        
<springProfile name="dev, staging">

</springProfile>

        
<springProfile name="!production">

</springProfile>