SpringVortexNotes

Logback 日志文件名带上应用名

zjc 于 2026-08-25 发布

代码环境

日志先要能被找到,然后才谈得上分析。这次把四个服务的 Logback 滚动文件名统一成“应用名 + 级别 + 日期”的格式。

应用名来自 Spring 属性:

<springProperty name="application"
                scope="context"
                source="spring.application.name"
                defaultValue="app"/>
<property name="LOG_HOME" value="logs/${application}"/>

https://github.com/springvortex/spring-cloud-alibaba/blob/release/v1.0.0/service-provider/src/main/resources/logback-spring.xml

各级别文件都使用同一命名规则:

<fileNamePattern>
    ${LOG_HOME}/info/${application}-info-%d{yyyy-MM-dd}.%i.log
</fileNamePattern>
<maxFileSize>1MB</maxFileSize>
<maxHistory>180</maxHistory>
<totalSizeCap>2GB</totalSizeCap>

https://github.com/springvortex/spring-cloud-alibaba/blob/release/v1.0.0/service-provider/src/main/resources/logback-spring.xml

以前的问题是格式不统一:DEBUG 文件里有构建时生成的 logPrefix 和小时分钟,INFO、WARN、ERROR 又叫 log-infolog-warnlog-error。排查跨服务问题时,文件名本身无法说明来源。调整后是:

service-provider-info-2026-08-25.0.log
service-gateway-error-2026-08-25.0.log
service-mail-warn-2026-08-25.0.log

日期只保留到天,同一天超过 1MB 时由 %i 递增序号。相比把小时分钟放进文件名,这种结构更适合 SizeAndTimeBasedRollingPolicy,也让按天检索保持稳定。

在 Docker 里,Compose 已把 /app/logs/<application> 挂到宿主机的 logs/providerlogs/gateway 等目录:

- ./logs/provider:/app/logs/service-provider

https://github.com/springvortex/spring-cloud-alibaba/blob/release/v1.0.0/deploy/docker-compose.yml

所以生产排查既可以用宿主机文件,也可以用 docker compose logs 看 stdout/stderr。要注意多实例场景:当前文件名按服务名区分,不按容器实例区分,长期横向扩容前应把容器 HOSTNAME 加入日志路径或文件名,避免多个 JVM 写同一组文件。

完整配置

四个可运行服务的 logback-spring.xml 结构一致,下面贴 Provider 的完整配置;Gateway、Consumer、Mail 会通过各自的 spring.application.name 得到自己的日志目录和文件名前缀。

<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="10 seconds" debug="false">
    <springProperty name="application" scope="context" source="spring.application.name" defaultValue="app"/>
    <springProperty name="port" scope="context" source="server.port" defaultValue="0"/>
    <contextName>logback</contextName>
    <property name="LOG_HOME" value="logs/${application}"/>
    <property name="CONSOLE_LOG_PATTERN"
              value="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%thread] [traceId=%X{traceId:-},spanId=%X{spanId:-}] [%highlight(%-5level)] [${application}:${port}] [%cyan(%logger{50})] [%msg]%n"/>
    <property name="FILE_LOG_PATTERN"
              value="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%thread] [traceId=%X{traceId:-},spanId=%X{spanId:-}] [%-5level] [${application}:${port}] [%logger{50}] [%msg]%n"/>

    <!-- DEBUG 级别文件输出 -->
    <appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <encoder>
            <Pattern>${FILE_LOG_PATTERN}</Pattern>
            <charset>UTF-8</charset>
        </encoder>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${LOG_HOME}/debug/${application}-debug-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <maxFileSize>1MB</maxFileSize>
            <maxHistory>180</maxHistory>
            <totalSizeCap>2GB</totalSizeCap>
        </rollingPolicy>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>DEBUG</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!-- INFO 级别文件输出(仅 INFO) -->
    <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <encoder>
            <Pattern>${FILE_LOG_PATTERN}</Pattern>
            <charset>UTF-8</charset>
        </encoder>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${LOG_HOME}/info/${application}-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <maxFileSize>1MB</maxFileSize>
            <maxHistory>180</maxHistory>
            <totalSizeCap>2GB</totalSizeCap>
        </rollingPolicy>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFO</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!-- WARN 级别文件输出 -->
    <appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <encoder>
            <Pattern>${FILE_LOG_PATTERN}</Pattern>
            <charset>UTF-8</charset>
        </encoder>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${LOG_HOME}/warn/${application}-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <maxFileSize>1MB</maxFileSize>
            <maxHistory>180</maxHistory>
            <totalSizeCap>2GB</totalSizeCap>
        </rollingPolicy>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>WARN</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!-- ERROR 级别文件输出 -->
    <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <encoder>
            <Pattern>${FILE_LOG_PATTERN}</Pattern>
            <charset>UTF-8</charset>
        </encoder>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${LOG_HOME}/error/${application}-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <maxFileSize>1MB</maxFileSize>
            <maxHistory>180</maxHistory>
            <totalSizeCap>2GB</totalSizeCap>
        </rollingPolicy>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!-- Logback AsyncAppender 只允许挂载一个下游 Appender,因此每个级别单独异步 -->
    <appender name="ASYNC_DEBUG_FILE" class="ch.qos.logback.classic.AsyncAppender">
        <queueSize>8192</queueSize>
        <discardingThreshold>0</discardingThreshold>
        <neverBlock>false</neverBlock>
        <includeCallerData>false</includeCallerData>
        <maxFlushTime>3000</maxFlushTime>
        <appender-ref ref="DEBUG_FILE"/>
    </appender>

    <appender name="ASYNC_INFO_FILE" class="ch.qos.logback.classic.AsyncAppender">
        <queueSize>8192</queueSize>
        <discardingThreshold>0</discardingThreshold>
        <neverBlock>false</neverBlock>
        <includeCallerData>false</includeCallerData>
        <maxFlushTime>3000</maxFlushTime>
        <appender-ref ref="INFO_FILE"/>
    </appender>

    <appender name="ASYNC_WARN_FILE" class="ch.qos.logback.classic.AsyncAppender">
        <queueSize>8192</queueSize>
        <discardingThreshold>0</discardingThreshold>
        <neverBlock>false</neverBlock>
        <includeCallerData>false</includeCallerData>
        <maxFlushTime>3000</maxFlushTime>
        <appender-ref ref="WARN_FILE"/>
    </appender>

    <appender name="ASYNC_ERROR_FILE" class="ch.qos.logback.classic.AsyncAppender">
        <queueSize>8192</queueSize>
        <discardingThreshold>0</discardingThreshold>
        <neverBlock>false</neverBlock>
        <includeCallerData>false</includeCallerData>
        <maxFlushTime>3000</maxFlushTime>
        <appender-ref ref="ERROR_FILE"/>
    </appender>

    <!-- 开发/测试环境:控制台同步输出,文件异步输出 -->
    <springProfile name="dev,test">
        <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
            <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
                <level>DEBUG</level>
            </filter>
            <encoder>
                <Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
                <charset>UTF-8</charset>
            </encoder>
        </appender>

        <!-- 业务代码输出 DEBUG,第三方依赖保持 INFO,避免噪声过大 -->
        <logger name="com.zjc" level="DEBUG"/>

        <root level="INFO">
            <appender-ref ref="STDOUT"/>
            <appender-ref ref="ASYNC_DEBUG_FILE"/>
            <appender-ref ref="ASYNC_INFO_FILE"/>
            <appender-ref ref="ASYNC_WARN_FILE"/>
            <appender-ref ref="ASYNC_ERROR_FILE"/>
        </root>
    </springProfile>

    <!-- 生产环境:仅保留异步文件输出 -->
    <springProfile name="prod">
        <root level="INFO">
            <appender-ref ref="ASYNC_DEBUG_FILE"/>
            <appender-ref ref="ASYNC_INFO_FILE"/>
            <appender-ref ref="ASYNC_WARN_FILE"/>
            <appender-ref ref="ASYNC_ERROR_FILE"/>
        </root>
    </springProfile>

</configuration>

https://github.com/springvortex/spring-cloud-alibaba/blob/release/v1.0.0/service-provider/src/main/resources/logback-spring.xml

经验总结

滚动文件名带上应用名,是最便宜的日志元数据。服务、级别、日期一眼可辨,再配合大小滚动和保留上限,日志才不会变成部署机上的黑洞。

评论

评论由 GitHub Discussions 承载,需要 GitHub 账号登录。