【Spring】日志

日志

日志结构说明

一条完整的日志记录通常包含以下几个核心组成部分,它们共同构成了日志的结构,帮助开发者快速定位问题。

日志结构示意图:
#mermaid-svg-M0GaRheeoRdEmvNB{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-M0GaRheeoRdEmvNB .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-M0GaRheeoRdEmvNB .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-M0GaRheeoRdEmvNB .error-icon{fill:#552222;}#mermaid-svg-M0GaRheeoRdEmvNB .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-M0GaRheeoRdEmvNB .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-M0GaRheeoRdEmvNB .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-M0GaRheeoRdEmvNB .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-M0GaRheeoRdEmvNB .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-M0GaRheeoRdEmvNB .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-M0GaRheeoRdEmvNB .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-M0GaRheeoRdEmvNB .marker{fill:#333333;stroke:#333333;}#mermaid-svg-M0GaRheeoRdEmvNB .marker.cross{stroke:#333333;}#mermaid-svg-M0GaRheeoRdEmvNB svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-M0GaRheeoRdEmvNB p{margin:0;}#mermaid-svg-M0GaRheeoRdEmvNB .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-M0GaRheeoRdEmvNB .cluster-label text{fill:#333;}#mermaid-svg-M0GaRheeoRdEmvNB .cluster-label span{color:#333;}#mermaid-svg-M0GaRheeoRdEmvNB .cluster-label span p{background-color:transparent;}#mermaid-svg-M0GaRheeoRdEmvNB .label text,#mermaid-svg-M0GaRheeoRdEmvNB span{fill:#333;color:#333;}#mermaid-svg-M0GaRheeoRdEmvNB .node rect,#mermaid-svg-M0GaRheeoRdEmvNB .node circle,#mermaid-svg-M0GaRheeoRdEmvNB .node ellipse,#mermaid-svg-M0GaRheeoRdEmvNB .node polygon,#mermaid-svg-M0GaRheeoRdEmvNB .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-M0GaRheeoRdEmvNB .rough-node .label text,#mermaid-svg-M0GaRheeoRdEmvNB .node .label text,#mermaid-svg-M0GaRheeoRdEmvNB .image-shape .label,#mermaid-svg-M0GaRheeoRdEmvNB .icon-shape .label{text-anchor:middle;}#mermaid-svg-M0GaRheeoRdEmvNB .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-M0GaRheeoRdEmvNB .rough-node .label,#mermaid-svg-M0GaRheeoRdEmvNB .node .label,#mermaid-svg-M0GaRheeoRdEmvNB .image-shape .label,#mermaid-svg-M0GaRheeoRdEmvNB .icon-shape .label{text-align:center;}#mermaid-svg-M0GaRheeoRdEmvNB .node.clickable{cursor:pointer;}#mermaid-svg-M0GaRheeoRdEmvNB .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-M0GaRheeoRdEmvNB .arrowheadPath{fill:#333333;}#mermaid-svg-M0GaRheeoRdEmvNB .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-M0GaRheeoRdEmvNB .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-M0GaRheeoRdEmvNB .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-M0GaRheeoRdEmvNB .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-M0GaRheeoRdEmvNB .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-M0GaRheeoRdEmvNB .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-M0GaRheeoRdEmvNB .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-M0GaRheeoRdEmvNB .cluster text{fill:#333;}#mermaid-svg-M0GaRheeoRdEmvNB .cluster span{color:#333;}#mermaid-svg-M0GaRheeoRdEmvNB div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-M0GaRheeoRdEmvNB .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-M0GaRheeoRdEmvNB rect.text{fill:none;stroke-width:0;}#mermaid-svg-M0GaRheeoRdEmvNB .icon-shape,#mermaid-svg-M0GaRheeoRdEmvNB .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-M0GaRheeoRdEmvNB .icon-shape p,#mermaid-svg-M0GaRheeoRdEmvNB .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-M0GaRheeoRdEmvNB .icon-shape .label rect,#mermaid-svg-M0GaRheeoRdEmvNB .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-M0GaRheeoRdEmvNB .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-M0GaRheeoRdEmvNB .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-M0GaRheeoRdEmvNB :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 时间戳
日志级别
线程信息
记录器名称
日志消息
异常堆栈

1. 时间戳

记录日志事件发生的精确时间,通常包含日期和具体时间(精确到毫秒或微秒)。

  • 格式示例2026-05-23 20:35:48.123
  • 作用:用于追溯问题发生的时间点,分析时间序列上的事件关联。

2. 日志级别

标识日志事件的严重程度,是日志分类和过滤的关键依据。

常见级别详解

从低到高,日志级别依次为:

级别 说明 典型场景
TRACE 最细粒度的追踪信息,通常用于开发阶段的详细调试,生产环境极少开启 方法入口/出口参数追踪
DEBUG 调试信息,记录程序运行过程中的关键变量、方法调用等,便于开发人员排查问题 变量值变化、SQL 语句打印
INFO 重要运行信息,如服务启动、用户登录、请求处理完成等。生产环境默认开启 服务启动完成、用户操作记录
WARN 警告信息,表示程序运行中出现了潜在问题或非预期情况,但不会影响系统正常运行 配置项缺失使用默认值、接口响应时间过长
ERROR 错误信息,表示发生了影响功能正常执行的错误,需要开发人员关注和处理 数据库连接失败、第三方服务调用异常
FATAL 致命错误,表示发生了严重故障,导致应用程序无法继续运行 内存溢出、核心配置加载失败

级别过滤机制

日志框架通过设置根日志级别 来控制输出:只会输出大于等于当前设置级别的日志。

Spring Boot 默认的日志级别是 INFO ,意味着默认只输出 INFO 及以上 的日志,所以 TRACEDEBUG 被过滤掉了。

java 复制代码
@RequestMapping("/log")
@RestController
public class LogController {

    private final static Logger logger = LoggerFactory.getLogger(LogController.class);

    @RequestMapping("/print")
    public String print(){
        logger.info("logger 打印日志");
        logger.trace("logger trace...");
        logger.debug("logger debug...");
        logger.info("logger info...");
        logger.warn("logger warn...");
        logger.error("logger error...");
        return "打印日志";
    }
}

application.properties 中添加一行来降低日志级别:

properties 复制代码
logging.level.org.example.logdemo.controller=TRACE

或者针对整个项目的包:

properties 复制代码
logging.level.org.example.logdemo=DEBUG

如果项目使用的是 application.yml 配置文件,对应的配置如下:

yml 复制代码
logging:
  level:
    org:
      example:
        logdemo:
          controller: TRACE

或者使用更简洁的包路径写法:

yml 复制代码
logging:
  level:
    org.example.logdemo.controller: TRACE

针对整个项目包:

yml 复制代码
logging:
  level:
    org.example.logdemo: DEBUG

不同环境的级别建议

环境 推荐级别 说明
开发环境 DEBUGTRACE 输出尽可能多的信息,便于调试
测试环境 DEBUG 兼顾信息量和性能
生产环境 INFO 只记录重要信息和错误,避免日志过多影响性能
线上紧急排查 临时调为 DEBUG 针对特定包或类开启,问题修复后恢复

3. 线程信息

记录产生该日志的线程名称或 ID。

  • 格式示例[main][http-nio-8080-exec-1]
  • 作用:在多线程应用中,帮助区分不同线程的执行路径,便于排查并发问题。

4. 日志记录器名称

通常是产生日志的类名或包名。

  • 格式示例com.example.service.UserService
  • 作用:快速定位日志来源,知道是哪段代码输出的日志。

5. 日志消息

日志的核心内容,描述具体发生了什么事件。

  • 内容 :可以是简单的字符串,也可以是包含占位符的动态消息(如 用户 {} 登录成功)。
  • 作用:提供问题或事件的详细描述。

6. 异常堆栈(可选)

当记录 ERROR 级别日志时,通常会附带异常堆栈信息。

  • 格式:以换行缩进形式展示异常类型、消息及调用链。
  • 作用:帮助开发者快速定位代码中抛出异常的具体位置和调用关系。

日志持久化

日志持久化是指将运行中的日志信息写入到文件或外部存储系统中,以便后续的排查、审计和监控。Spring Boot 提供了简洁的配置方式来实现日志的文件输出。

日志持久化有两种配置方式:

  1. 配置日志文件名 :通过 logging.file.name 指定日志文件的完整路径和名称。
  2. 配置日志存储目录 :通过 logging.file.path 指定日志文件的存放目录,文件名默认为 spring.log

优先级说明 :如果两个配置同时存在,以 logging.file.name 为准。

yml 复制代码
logging:
  file:
    # path: logger/          # 方式一:仅指定目录,文件名默认为 spring.log
    name: logger/log.log     # 方式二:指定完整文件名(优先级更高)
  • 相对路径logger/log.log 表示在当前项目根目录下创建 logger 文件夹,日志写入 log.log 文件。
  • 绝对路径/var/log/myapp/app.log 表示写入系统指定路径。

日志滚动策略

生产环境中,日志文件会持续增长,如果不加以控制,可能占满磁盘空间。因此需要配置日志滚动策略,当日志文件达到指定大小时自动归档。

yml 复制代码
logging:
  file:
    name: logger/log.log 
  logback:
    rollingpolicy:
      max-file-size: 1KB                    # 单个日志文件最大 1KB
      file-name-pattern: ${LOG_FILE}.%d{yyyy-MM-dd}.%i  # 归档文件名格式

参数说明

参数 说明 示例值
max-file-size 单个日志文件的最大大小,超过后自动滚动 10MB100MB
file-name-pattern 归档文件的命名规则 ${LOG_FILE}.%d{yyyy-MM-dd}.%i
max-history 保留的历史归档文件数量(可选) 7(保留最近 7 天)
total-size-cap 所有归档文件的总大小上限(可选) 1GB

归档文件名示例

  • log.log.2026-05-24.0(当天第一个归档)
  • log.log.2026-05-24.1(当天第二个归档)
  • log.log.2026-05-23.0(前一天归档)

运行结果

日志框架

在 Java 开发中,日志框架是项目不可或缺的基础设施。一个优秀的日志框架能够帮助开发者高效地记录、管理和分析系统运行信息。目前主流的 Java 日志体系分为日志门面日志实现两部分。

日志门面

日志门面(Facade)提供了一套统一的日志记录 API,让开发者无需关心底层具体的日志实现。当需要切换日志框架时,只需更换底层实现,而无需修改业务代码。

门面框架 说明
SLF4J(Simple Logging Facade for Java) 目前最流行的日志门面,提供简洁的占位符 {} 格式化,性能优于字符串拼接
Apache Commons Logging(JCL) 早期的日志门面,Spring 框架默认使用,但存在类加载器问题

推荐 :新项目建议直接使用 SLF4J,它已成为 Java 日志领域的事实标准。

日志实现

日志实现是真正执行日志输出、格式化、存储等工作的底层框架。

实现框架 说明
Logback SLF4J 的原生实现,性能优异,功能强大,Spring Boot 默认使用
Log4j2 Apache 推出的高性能日志框架,支持异步日志,性能比 Logback 更高
JUL(java.util.logging) JDK 自带的日志实现,功能较弱,一般不直接使用

日志门面与实现的关系

#mermaid-svg-ZlENaKjpuMcyqcby{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ZlENaKjpuMcyqcby .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ZlENaKjpuMcyqcby .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ZlENaKjpuMcyqcby .error-icon{fill:#552222;}#mermaid-svg-ZlENaKjpuMcyqcby .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ZlENaKjpuMcyqcby .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ZlENaKjpuMcyqcby .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ZlENaKjpuMcyqcby .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ZlENaKjpuMcyqcby .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ZlENaKjpuMcyqcby .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ZlENaKjpuMcyqcby .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ZlENaKjpuMcyqcby .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ZlENaKjpuMcyqcby .marker.cross{stroke:#333333;}#mermaid-svg-ZlENaKjpuMcyqcby svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ZlENaKjpuMcyqcby p{margin:0;}#mermaid-svg-ZlENaKjpuMcyqcby .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ZlENaKjpuMcyqcby .cluster-label text{fill:#333;}#mermaid-svg-ZlENaKjpuMcyqcby .cluster-label span{color:#333;}#mermaid-svg-ZlENaKjpuMcyqcby .cluster-label span p{background-color:transparent;}#mermaid-svg-ZlENaKjpuMcyqcby .label text,#mermaid-svg-ZlENaKjpuMcyqcby span{fill:#333;color:#333;}#mermaid-svg-ZlENaKjpuMcyqcby .node rect,#mermaid-svg-ZlENaKjpuMcyqcby .node circle,#mermaid-svg-ZlENaKjpuMcyqcby .node ellipse,#mermaid-svg-ZlENaKjpuMcyqcby .node polygon,#mermaid-svg-ZlENaKjpuMcyqcby .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ZlENaKjpuMcyqcby .rough-node .label text,#mermaid-svg-ZlENaKjpuMcyqcby .node .label text,#mermaid-svg-ZlENaKjpuMcyqcby .image-shape .label,#mermaid-svg-ZlENaKjpuMcyqcby .icon-shape .label{text-anchor:middle;}#mermaid-svg-ZlENaKjpuMcyqcby .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ZlENaKjpuMcyqcby .rough-node .label,#mermaid-svg-ZlENaKjpuMcyqcby .node .label,#mermaid-svg-ZlENaKjpuMcyqcby .image-shape .label,#mermaid-svg-ZlENaKjpuMcyqcby .icon-shape .label{text-align:center;}#mermaid-svg-ZlENaKjpuMcyqcby .node.clickable{cursor:pointer;}#mermaid-svg-ZlENaKjpuMcyqcby .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ZlENaKjpuMcyqcby .arrowheadPath{fill:#333333;}#mermaid-svg-ZlENaKjpuMcyqcby .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ZlENaKjpuMcyqcby .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ZlENaKjpuMcyqcby .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZlENaKjpuMcyqcby .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ZlENaKjpuMcyqcby .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZlENaKjpuMcyqcby .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ZlENaKjpuMcyqcby .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ZlENaKjpuMcyqcby .cluster text{fill:#333;}#mermaid-svg-ZlENaKjpuMcyqcby .cluster span{color:#333;}#mermaid-svg-ZlENaKjpuMcyqcby div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ZlENaKjpuMcyqcby .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ZlENaKjpuMcyqcby rect.text{fill:none;stroke-width:0;}#mermaid-svg-ZlENaKjpuMcyqcby .icon-shape,#mermaid-svg-ZlENaKjpuMcyqcby .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZlENaKjpuMcyqcby .icon-shape p,#mermaid-svg-ZlENaKjpuMcyqcby .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ZlENaKjpuMcyqcby .icon-shape .label rect,#mermaid-svg-ZlENaKjpuMcyqcby .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZlENaKjpuMcyqcby .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ZlENaKjpuMcyqcby .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ZlENaKjpuMcyqcby :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 应用程序代码
SLF4J 门面 API
Logback
Log4j2
JUL
日志输出

注解输出日志:@Slf4j⭐

java 复制代码
@RequestMapping("/log")
@RestController
public class LogController {

    private final static Logger logger = LoggerFactory.getLogger(LogController.class);

    @RequestMapping("/print")
    public String print(){
        logger.info("logger 打印日志");
        logger.trace("logger trace...");
        logger.debug("logger debug...");
        logger.info("logger info...");
        logger.warn("logger warn...");
        logger.error("logger error...");
        return "打印日志";
    }
}
 
java 复制代码
import lombok.extern.slf4j.Slf4j;

@Slf4j
@RequestMapping("/log")
@RestController
public class LogController {

    @RequestMapping("/print")
    public String print(){
        log.info("logger 打印日志");
        log.trace("logger trace...");
        log.debug("logger debug...");
        log.info("logger info...");
        log.warn("logger warn...");
        log.error("logger error...");
        return "打印日志";
    }
}
 

使用 @Slf4j 注解后,代码发生了以下变化:

变化点 使用前 使用后
导入语句 需手动导入 org.slf4j.Loggerorg.slf4j.LoggerFactory 只需导入 lombok.extern.slf4j.Slf4j
类注解 添加 @Slf4j 注解
Logger 声明 手动声明 private final static Logger logger = LoggerFactory.getLogger(LogController.class); 由 Lombok 自动生成,无需手动编写
变量名 logger log(Lombok 自动生成的变量名为 log
代码简洁度 每个类都需要重复编写 Logger 声明代码 一行注解搞定,代码更简洁

核心变化@Slf4j 在编译时自动生成 private static final Logger log = LoggerFactory.getLogger(当前类.class);,省去了手动声明 Logger 的样板代码,让开发者专注于业务逻辑。

【Spring】Lombok

为什么使用 SLF4J + Logback?

  1. 解耦:代码只依赖 SLF4J API,底层实现可随时切换。
  2. 性能:Logback 经过充分优化,性能优于 Log4j 1.x。
  3. 自动配置:Spring Boot 默认集成 SLF4J + Logback,开箱即用。
  4. 占位符支持 :SLF4J 的 {} 占位符在参数较多时性能优于字符串拼接。
相关推荐
我是唐青枫1 小时前
MySQL EXISTS 详解:存在性判断、NOT EXISTS 与实战示例
数据库·mysql
雪度娃娃1 小时前
转向现代C++——优先选用删除函数而非private未定义函数
java·jvm·c++
Kurisu5751 小时前
深度拆解:从 Linux 内核 Namespace 与 Cgroups 洞察容器技术的底层本质
java·linux·运维
罗超驿1 小时前
11.LeetCode 1004. 最大连续1的个数 III | 滑动窗口解法详解(Java)
java·算法·leetcode
努力发光的程序员1 小时前
面试官与程序员谢飞机的3轮Java大厂面试问答实录:涵盖Spring Boot、微服务与数据库技术
java·jvm·spring boot·redis·面试·hibernate·microservices
橙淮1 小时前
并发编程(四)
java·jvm
weixin_468466851 小时前
Airtable 零基础快速上手与实战指南
数据库·人工智能·python·深度学习·ai·大模型
凯瑟琳.奥古斯特1 小时前
10道数据库原理精选题
开发语言·数据库·职场和发展·数据库开发
z落落1 小时前
C# Stack栈 / Queue队列+所有集合 终极一页汇总(全覆盖、零遗漏)
java·开发语言·c#