SpringBoot生产级日志配置

Spring Boot 生产级日志配置指南

适用:Spring Boot 3.x(Java 17)、单体应用、日志落服务器文件。 当前依赖:lombok + spring-boot-starter-web ------ 无需任何额外日志依赖


0.:需要哪些依赖?

只需要 lombok + spring-boot-starter-web,日志依赖一个都不用加。

你需要的能力 来源
slf4j-api(日志门面) spring-boot-starter-webspring-boot-starterspring-boot-starter-logging 传递带入
logback-classic(日志实现) 同上,传递带入
@Slf4j 注解 lombok

xml

复制代码
<!-- 全部需要的依赖,就这俩 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <scope>provided</scope>
</dependency>

想亲眼验证:mvn dependency:tree,会看到 ch.qos.logback:logback-classicorg.slf4j:slf4j-api 挂在 spring-boot-starter-web → ...-starter → ...-starter-logging 下面 ------ 虽然你 pom 里一个字都没写它们。

可选依赖(本指南不需要,见文末「附录」):微服务链路追踪、JSON 日志(ELK/Loki)。


1. 代码里怎么打日志

只用 SLF4J 门面 (@Slf4j 生成的就是它),不要直接 import ch.qos.logback.*

java

复制代码
@Slf4j                                   // Lombok 生成 private static final Logger log
@Service
public class OrderService {
    public void create(Long userId) {
        log.info("创建订单 userId={}", userId);             // ✅ {} 占位,别用 "+" 拼接
        try {
            // ... 业务
        } catch (Exception e) {
            log.error("创建订单失败 userId={}", userId, e);   // ✅ 异常对象放最后,保留堆栈
        }
    }
}

两条铁律

  1. {} 占位:级别没开时不拼字符串,省性能;也避免拼接出错。
  2. 异常对象放最后一个参数(log.error(msg, e)):别写 log.error(e.getMessage()),那样会丢堆栈。

级别由高到低:error > warn > info > debug > trace,生产环境 root 一般设 INFO


2. logback-spring.xml

放在 src/main/resources/logback-spring.xml

文件名必须logback-spring.xml(带 -spring),否则用不了 <springProfile> / <springProperty>。 Spring Boot 启动时自动加载它。

xml

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!--
  生产级 Logback 配置(单体应用 / 落文件)。
  ① 文件名必须 logback-spring.xml,否则 <springProfile>/<springProperty> 不生效。
  ② 放 src/main/resources/ 下,Spring Boot 启动自动加载。
-->
<configuration>

    <!-- ============ 1. 从 application.yml 读变量 ============ -->
    <!-- source=yml 里的 key;defaultValue=yml 没配时的兜底。路径可按环境在 yml 改 -->
    <springProperty name="LOG_PATH" source="logging.file.path" defaultValue="/opt/app/logs"/>
    <springProperty name="APP_NAME" source="spring.application.name" defaultValue="app"/>

    <!-- ============ 2. 日志格式 ============ -->
    <!--
      %d 时间 | %thread 线程 | %-5level 级别(左对齐占5位)
      %logger{36} 类名(最长36字符) | %msg 内容 | %n 换行
      (单体应用不需要 traceId,所以这里没有 %X{traceId};要链路追踪见文末附录)
    -->
    <property name="PATTERN"
        value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"/>

    <!-- ============ 3. 控制台(开发看 + 容器收 stdout)============ -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${PATTERN}</pattern>
            <charset>UTF-8</charset>          <!-- 防中文乱码 -->
        </encoder>
    </appender>

    <!-- ============ 4. 主日志文件(滚动 + 自动删除,核心)============ -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/${APP_NAME}.log</file>   <!-- 当前正在写的文件 -->

        <!-- SizeAndTimeBased:同时按"天"和"大小"滚动 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <!--
              归档命名:%d 按天,%i 同一天内按大小切的序号,.gz 自动压缩。
              %d{yyyy-MM-dd} 决定滚动周期=天 → maxHistory 单位也是"天"。
            -->
            <fileNamePattern>${LOG_PATH}/${APP_NAME}.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>

            <maxFileSize>100MB</maxFileSize>     <!-- 单文件满 100MB 就切,%i 递增(只切分,不删) -->
            <maxHistory>30</maxHistory>          <!-- ★自动删除①:按时间,只留最近30天,更早的删 -->
            <totalSizeCap>10GB</totalSizeCap>    <!-- ★自动删除②:按总量,所有归档超10GB从最老删 -->
            <cleanHistoryOnStart>true</cleanHistoryOnStart> <!-- 启动时也清一次过期文件 -->
        </rollingPolicy>

        <encoder>
            <pattern>${PATTERN}</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>

    <!-- ============ 5. 单独的 ERROR 文件(排障只看错误)============ -->
    <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/${APP_NAME}-error.log</file>
        <!-- LevelFilter:只放行 ERROR,其它级别全拒绝 -->
        <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.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/${APP_NAME}-error.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
            <maxFileSize>100MB</maxFileSize>
            <maxHistory>60</maxHistory>          <!-- 错误日志想留久点,留60天 -->
            <totalSizeCap>2GB</totalSizeCap>
        </rollingPolicy>
        <encoder>
            <pattern>${PATTERN}</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>

    <!-- ============ 6. 异步包装(降低写日志对业务线程的拖累)============ -->
    <appender name="ASYNC_FILE" class="ch.qos.logback.classic.AsyncAppender">
        <queueSize>1024</queueSize>              <!-- 缓冲队列大小,日志先入队、后台线程落盘 -->
        <!--
          队列剩余低于此阈值时丢弃 TRACE/DEBUG/INFO(保留 WARN/ERROR)。
          0 = 任何级别都不丢(极端高压会阻塞业务线程,但日志最全);
          想高吞吐、能容忍丢低级别 → 设成 queueSize 的 20%(如 200)。
        -->
        <discardingThreshold>0</discardingThreshold>
        <appender-ref ref="FILE"/>               <!-- 异步包住上面的 FILE -->
    </appender>

    <!-- ============ 7. 按 profile 装配 ============ -->
    <!-- 开发:只打控制台,IDEA 里直接看,不落文件 -->
    <springProfile name="dev">
        <root level="INFO">
            <appender-ref ref="CONSOLE"/>
        </root>
    </springProfile>

    <!-- 生产:控制台(给平台采集)+ 异步主文件 + 单独 error 文件 -->
    <springProfile name="prod">
        <root level="INFO">
            <appender-ref ref="CONSOLE"/>
            <appender-ref ref="ASYNC_FILE"/>
            <appender-ref ref="ERROR_FILE"/>
        </root>
    </springProfile>

</configuration>

3. application.yml(配套)

yaml

复制代码
spring:
  application:
    name: demo                 # 进日志(${APP_NAME});将来要追踪也用它
  profiles:
    active: prod               # 决定 logback 走 dev 段还是 prod 段(上线设 prod)

logging:
  file:
    path: /opt/app/logs        # logback 里 ${LOG_PATH} 读它;给"目录",不给文件名
  level:                       # ★级别放 yml 改起来方便,会覆盖 xml <root> 的默认
    root: INFO                 # 全局默认级别
    com.example.demo: INFO     # 你自己的包,临时排障时改 DEBUG
    org.springframework: WARN  # 压低框架噪音

设计取舍 :结构(appender / 滚动 / 异步 / error 文件)放 xml(yml 表达不了这些); 级别放 yml(改得频繁,放 yml 不用动 xml,还能用 application-prod.yml 按环境覆盖)。


4. "自动删除"是怎么实现的

配置项 作用 是否删文件
maxFileSize 单文件超过大小就切分成新文件 否(只切分)
maxHistory 时间:只保留最近 N 天,更早的归档 ✅ 自动删
totalSizeCap 总量:所有归档超过上限,从最老删 ✅ 自动删

maxHistory + totalSizeCap 一起兜底:既不留太久、也不占太多。 这两个是防止磁盘被日志撑爆的关键,生产最容易漏、漏了最致命。


5. 生产必守清单

  • 滚动 + 保留 + 总量封顶三件套必配,否则磁盘迟早爆。
  • 配置文件名用 logback-spring.xml ,不要 logback.xml(后者用不了 Spring 的 profile / 属性)。
  • 历史日志 .gz 压缩,省空间。
  • 生产用绝对路径 (/opt/app/logs),可预期、不随启动目录漂。
  • 别打敏感信息(密码、token、手机号 / 身份证全量),该脱敏脱敏。
  • 容器 / K8s 部署 :只留 CONSOLE(stdout),交平台(ELK / Loki)采集,别在容器里写文件 (重启就没了);传统 ECS / 物理机才落文件。
  • 启动用 --spring.profiles.active=prod 走生产那套(或 yml 里默认)。

附录:什么时候才加那些可选依赖(本指南都不需要)

A. 链路追踪 traceId ------ 仅微服务需要

单体应用不需要。当一个请求要跨多个服务 、想用同一个 traceId 把各服务日志串起来时才加:

xml

复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-tracing-bridge-brave</artifactId>
</dependency>

加完后,把 pattern 改回带 [%X{traceId:-}] 的版本,traceId 才会有值。

备用:单体里若只想给每个请求加个请求 ID,不必上 micrometer ------ 写个 Filter 往 MDC 塞个 UUID,pattern 里用 %X{你的key} 引用即可,同样零额外依赖。

B. JSON 日志 ------ 仅接 ELK / Loki / Grafana 需要

直接在服务器看文本日志的话不需要。要把日志送进集中式平台、需要 JSON 格式时才加:

xml

复制代码
<dependency>
    <groupId>net.logstash.logback</groupId>
    <artifactId>logstash-logback-encoder</artifactId>
    <version>8.0</version> <!-- 版本对齐你的 logback / Spring Boot 3.x -->
</dependency>

然后把对应 appender 的 <encoder> 换成 LogstashEncoder 即可。

相关推荐
碎碎念_4921 小时前
SpringBoot和MyBatis框架·速通版
spring boot·后端·mybatis
ch.ju1 小时前
Java Programming Chapter 4——Inherited call
java·开发语言
是有头发的程序猿1 小时前
竞品分析 + 用户洞察自动化|基于 item_review 评论接口 + 多 AI Agent 实现淘宝评论全量采集与智能分析(附python源码)
java·python·自动化
秋天的一阵风1 小时前
AGENTS.md:你的AI代码助手,需要一份"项目说明书"
前端·后端·ai编程
凤凰院凶涛QAQ1 小时前
《Java版数据结构 & 集合类剖析》链表与LinkedList:节点手拉手,增删不用愁
java·数据结构·链表
jserTang1 小时前
手撕 Claude Code-7:自动压缩与记忆恢复
前端·后端
嘟嘟07172 小时前
从 TypeScript 到 Bun:一份前端开发者的效率进阶笔记
后端
用户7508837061952 小时前
循环依赖加 @Lazy 后异常漂移?Spring 三级缓存为什么没兜住?
后端