【框架】日志-SLF4J+Logback

文章目录

  • 一、SLF4J和Logback分别是什么?
    • [1. SLF4J 是什么?](#1. SLF4J 是什么?)
    • [2. Logback 是什么?](#2. Logback 是什么?)
    • [3. 两者到底是什么关系?](#3. 两者到底是什么关系?)
    • [4. 为什么不直接使用 Logback?](#4. 为什么不直接使用 Logback?)
    • [5. 一句话区分](#5. 一句话区分)
  • 二、怎么在项目中使用SLF4J和Logback?
  • 三、拓展:使用注解
    • [1. `@Slf4j` 是谁提供的?](#1. @Slf4j 是谁提供的?)
    • [2. 它到底生成了什么?](#2. 它到底生成了什么?)
    • [3. 这个过程发生在哪里?](#3. 这个过程发生在哪里?)
    • [4. Lombok 到底生成了什么?](#4. Lombok 到底生成了什么?)
    • [5. 那 `@Slf4j` 和 Logback 有关系吗?](#5. 那 @Slf4j 和 Logback 有关系吗?)
    • [6. 为什么写了 `@Slf4j` 就可以直接使用 `log`?](#6. 为什么写了 @Slf4j 就可以直接使用 log?)
    • [7. 为什么有时候 `@Slf4j` 不生效?](#7. 为什么有时候 @Slf4j 不生效?)
    • [8. 你可以用一个非常简单的方式验证](#8. 你可以用一个非常简单的方式验证)
    • [9. 最后把整个链条串起来](#9. 最后把整个链条串起来)

一、SLF4J和Logback分别是什么?

可以把它们先粗暴地理解成:

SLF4J = 规定"怎么记日志"的接口/门面

Logback = 真正负责"把日志记下来"的实现

人类又一次把"打印一句话"拆成两套东西,但这个拆分其实非常有价值。

1. SLF4J 是什么?

SLF4J 全称是 Simple Logging Facade for Java

它本身不是具体的日志实现,而是一层抽象接口。

比如你写:

java 复制代码
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Logger log = LoggerFactory.getLogger(UserService.class);

log.info("用户登录");

这里的:

java 复制代码
Logger log

就是 SLF4J 提供的 Logger 接口

你调用:

java 复制代码
log.info("用户登录");

实际上是在说:

"我想记录一条 INFO 级别的日志。"

但 SLF4J 不负责决定这句话最终写到哪里

它把这个问题交给下面的具体日志实现。


2. Logback 是什么?

Logback 是一个真正的日志框架实现。

它负责具体执行:

这条日志怎么处理、输出到哪里、输出什么格式、是否保存文件、文件多大时切换等。

例如:

text 复制代码
log.info("用户登录");

最终可能被 Logback 输出成:

text 复制代码
2026-08-19 15:20:31 INFO  UserService - 用户登录

并且可以输出到:

text 复制代码
控制台
   ↓
ConsoleAppender

或者:

text 复制代码
日志文件
   ↓
FileAppender

甚至按日期生成:

text 复制代码
logs/
├── app.2026-08-18.log
├── app.2026-08-19.log
└── app.log

这些才是 Logback 干的活。


3. 两者到底是什么关系?

可以画成:

text 复制代码
你的业务代码
     │
     │ log.info("用户登录")
     ↓
SLF4J
(Logger 接口)
     │
     │ 把日志请求交给具体实现
     ↓
Logback
(具体实现)
     │
     ├──→ 控制台
     │
     ├──→ 日志文件
     │
     └──→ 其他输出

所以你之前理解的那句话其实已经抓到核心了:

代码对外使用的是 SLF4J 的 Logger 接口,真正干活的是 Logback。


4. 为什么不直接使用 Logback?

假设你的业务代码直接写:

java 复制代码
ch.qos.logback.classic.Logger log;

那么你的业务代码就和 Logback 强绑定了。

以后想换成 Log4j2,你可能需要改大量代码。

而使用:

java 复制代码
org.slf4j.Logger

你的业务代码只依赖 SLF4J 的抽象

text 复制代码
业务代码
   ↓
SLF4J Logger
   ↓
Logback

以后换实现:

text 复制代码
业务代码
   ↓
SLF4J Logger
   ↓
Log4j2

业务代码基本不用动。

这就是它非常重要的价值:

面向接口,而不是面向具体实现。


5. 一句话区分

SLF4J Logback
是什么 日志门面/抽象 日志实现
主要作用 定义日志 API 真正处理日志
提供 Logger 实现它
决定日志输出到哪里
控制日志格式
控制文件滚动
可以替换 本身就是抽象层 可以换成 Log4j2 等

你可以记成一句非常形象的话:

SLF4J 负责"说我要记日志",Logback 负责"行,我来把它记下来"。

再往下理解,就是你前面一直在追的那条线:

Logger 接口 → LoggerFactory → SLF4J 与 Logback 的绑定 → Logback 创建具体 Logger 对象 → Appender 输出日志。

这条链搞明白,Java 日志这块基本就从"会配 XML"进入"知道它为什么能跑"的阶段了。

二、怎么在项目中使用SLF4J和Logback?

1、引入依赖

  1. maven项目,如果使用了springboot,那么springboot集成的日志框架就是SLF4J+Logback,不需要额外引入依赖。

  2. 若不是springboot项目,需引入依赖如下:

xml 复制代码
 <!-- SLF4J 门面 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>2.0.13</version>
        </dependency>
        <!-- Logback 实现,自动绑定 SLF4J -->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.5.6</version>
        </dependency>

2、加入配置

在src->main->resources路径下,增加logback.xml

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
   <configuration>
        <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
            <encoder>
                <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
            </encoder>
        </appender>
        <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <file>logs/app.log</file>
            <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
                <fileNamePattern>logs/app.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
                <maxFileSize>10MB</maxFileSize>
                <maxHistory>30</maxHistory>
            </rollingPolicy>
            <encoder>
                <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
            </encoder>
        </appender>
        <root level="INFO">
            <appender-ref ref="CONSOLE" />
            <appender-ref ref="FILE" />
        </root>
    </configuration>

3、使用日志

新建Logger对象:

java 复制代码
Logger log = LoggerFactory.getLogger(ClientApplication.class);

打印不同级别的日志:

java 复制代码
log.info("===info:开始组装machine注册信息===");
        log.debug("===debug:开始组装machine注册信息===");
        log.warn("===warn:开始组装machine注册信息===");
        log.error("===error:开始组装machine注册信息===");

例子如下:

java 复制代码
public class ClientApplication extends Application {
    private static final Logger log = LoggerFactory.getLogger(ClientApplication.class);

    public static void main(String[] args){
        launch(args);
    }

    @Override
    public void start(Stage stage){

        log.info("===info:开始组装machine注册信息===");
        log.debug("===debug:开始组装machine注册信息===");
        log.warn("===warn:开始组装machine注册信息===");
        log.error("===error:开始组装machine注册信息===");

        MachineRegisterDTO dto = new MachineRegisterDTO();
        dto.setMachineName(MachineInfoUtil.getMachineName());
        dto.setIp(MachineInfoUtil.getIp());
        dto.setMac(MachineInfoUtil.getMac());
        dto.setStatus("ONLINE");


        register(dto);


        stage.show();
    }
 }

日志打印效果:

对比两种日志打印方式效果:

三、拓展:使用注解

@Slf4j 能生效,核心其实就一句话:

它不是 SLF4J 自己提供的运行时魔法,而是 Lombok 在编译阶段帮你"生成了 Logger 代码"。

你之前理解的:

java 复制代码
private static final Logger log =
    LoggerFactory.getLogger(ClientApplication.class);

和:

java 复制代码
@Slf4j
public class ClientApplication {
}

本质上是同一件事。只是前者你手写,后者 Lombok 替你写。人类终于把"写一行固定代码"也自动化了。


1. @Slf4j 是谁提供的?

Lombok

java 复制代码
import lombok.extern.slf4j.Slf4j;

注意这里:

text 复制代码
@Slf4j
   ↓
Lombok
   ↓
编译阶段生成代码
   ↓
SLF4J Logger
   ↓
Logback

所以不要把 @Slf4j 误认为是 SLF4J 的注解。


2. 它到底生成了什么?

你写:

java 复制代码
@Slf4j
public class UserService {

    public void login() {
        log.info("用户登录");
    }
}

Lombok 在编译的时候,等价于帮你加入:

java 复制代码
public class UserService {

    private static final Logger log =
        LoggerFactory.getLogger(UserService.class);

    public void login() {
        log.info("用户登录");
    }
}

所以:

java 复制代码
@Slf4j

最重要的作用其实就是:

帮你生成 log 这个 Logger 对象。


3. 这个过程发生在哪里?

这里是理解 @Slf4j 的关键。

不是:

text 复制代码
程序启动
   ↓
发现 @Slf4j
   ↓
动态生成 Logger

而主要是:

text 复制代码
你写 Java 源代码
       ↓
       ↓
   @Slf4j
       ↓
Lombok 编译器插件
       ↓
修改/增强 AST
       ↓
生成 Logger 字段
       ↓
Java 编译器编译
       ↓
.class 文件
       ↓
程序运行

也就是说:

@Slf4j 主要发生在编译阶段,而不是运行阶段。


4. Lombok 到底生成了什么?

例如:

java 复制代码
@Slf4j
public class UserService {

    public void login() {
        log.info("用户登录");
    }
}

可以把 Lombok 理解成在编译的时候偷偷给你塞进去:

java 复制代码
private static final org.slf4j.Logger log =
    org.slf4j.LoggerFactory.getLogger(UserService.class);

然后你的:

java 复制代码
log.info("用户登录");

就有东西可以调用了。

所以整个关系变成:

text 复制代码
             Lombok
                │
                │ 编译阶段生成
                ↓
        Logger log = ...
                │
                ↓
        SLF4J Logger
                │
                │ 找具体实现
                ↓
             Logback
                │
                ↓
       控制台 / 文件 / ...

5. 那 @Slf4j 和 Logback 有关系吗?

间接有关系,但 @Slf4j 本身并不依赖 Logback。

这是非常重要的一点。

例如:

java 复制代码
@Slf4j
public class UserService {

    public void login() {
        log.info("用户登录");
    }
}

Lombok生成的大概是:

java 复制代码
private static final Logger log =
    LoggerFactory.getLogger(UserService.class);

这里的:

java 复制代码
Logger
LoggerFactory

来自:

text 复制代码
SLF4J

而不是 Logback。

然后运行的时候:

text 复制代码
log.info(...)
     ↓
SLF4J Logger
     ↓
SLF4J 找到具体日志实现
     ↓
Logback
     ↓
真正输出日志

因此:

Lombok

负责:

帮你生成 Logger 代码

SLF4J

负责:

定义 Logger 接口和统一调用方式

Logback

负责:

真正实现日志输出


6. 为什么写了 @Slf4j 就可以直接使用 log

因为 Lombok帮你生成了这个字段:

java 复制代码
private static final Logger log =
    LoggerFactory.getLogger(UserService.class);

所以:

java 复制代码
@Slf4j
public class UserService {

    public void login() {
        log.info("登录");
    }
}

编译器最终看到的效果可以近似理解成:

java 复制代码
public class UserService {

    private static final Logger log =
        LoggerFactory.getLogger(UserService.class);

    public void login() {
        log.info("登录");
    }
}

所以 log 并不是凭空出现的。


7. 为什么有时候 @Slf4j 不生效?

因为这里实际上有几个环节。

你需要:

text 复制代码
Lombok
  ↓
编译器识别 @Slf4j
  ↓
生成 Logger
  ↓
SLF4J
  ↓
找到日志实现
  ↓
Logback

任何地方出问题,都可能导致问题。

比如:

第一种:Lombok没配置

java 复制代码
import lombok.extern.slf4j.Slf4j;

都找不到。


第二种:IDE没有正确启用 Lombok

可能出现:

text 复制代码
Cannot resolve symbol 'log'

虽然 Maven 编译可能又能正常运行。


第三种:没有 SLF4J

Lombok生成的是类似:

java 复制代码
LoggerFactory.getLogger(...)

它还是需要 SLF4J。


第四种:没有日志实现

例如 SLF4J 存在:

text 复制代码
你的代码
 ↓
SLF4J

但没有:

text 复制代码
Logback

之类的实现。

这时候你可能会看到类似:

text 复制代码
No SLF4J providers were found

日志框架这套东西就像接力赛,接口、实现、绑定,一个掉棒,最后都是程序在抱怨。


8. 你可以用一个非常简单的方式验证

在 IDEA 里找到:

java 复制代码
@Slf4j
public class UserService {
    
    public void test() {
        log.info("hello");
    }
}

然后使用 IDEA 的:

View → Show Bytecode

或者把编译后的 .class 反编译。

你最终会看到类似:

java 复制代码
private static final Logger log =
    LoggerFactory.getLogger(UserService.class);

这就是 @Slf4j 的核心秘密。


9. 最后把整个链条串起来

你现在可以把它记成这一条:

text 复制代码
                编译阶段
                   │
                   ▼
              @Slf4j
                   │
                   ▼
                Lombok
                   │
                   │ 自动生成
                   ▼
private static final Logger log =
    LoggerFactory.getLogger(UserService.class);
                   │
                   ▼
                 SLF4J
             (日志门面/接口)
                   │
                   │ 运行时找到实现
                   ▼
                Logback
             (具体日志实现)
                   │
          ┌────────┴────────┐
          ▼                 ▼
       控制台              文件

所以你之前的理解可以进一步升级成:

@Slf4j 是 Lombok 提供的编译期注解,它自动生成一个 SLF4J 的 Logger 字段;程序运行时,这个 Logger 再通过 SLF4J 的机制交给 Logback 等具体实现处理。

最关键的是区分三个层次:

@Slf4j ≠ SLF4J ≠ Logback

而是:

Lombok负责"帮你写代码" → SLF4J负责"统一接口" → Logback负责"真正干活"。

相关推荐
gugucoding4 小时前
57. 【Java】日志框架:SLF4J与Logback
java·开发语言·logback
山甫aa1 天前
日志技术 Logback + Slf4j —— 从零开始的 Web 后端学习
java·后端·学习·web·logback
gis开发之家7 天前
日志怎么打才专业?Spring Boot 4 日志体系与 Logback 配置(生产级实战)
java·spring boot·后端·logback
带刺的坐椅14 天前
Solon 的日志设计哲学:不只是用 SLF4J 做统一门面
java·logback·solon·log4j2
sun༒1 个月前
Logback 日志框架总结:从入门到实战
单元测试·logback
.柒宇.1 个月前
ELK日志分析系统实战指南:Elasticsearch搭建与入门(8.18.8版本)
运维·elk·elasticsearch·logback·日志系统
hdsoft_huge1 个月前
SpringBoot系列17:日志体系Logback配置,日志分割、脱敏、异常堆栈收集规范(生产级落地)
java·spring boot·logback
倒流时光三十年1 个月前
Logback 系列(13):生产环境配置建议清单
logback
倒流时光三十年1 个月前
Logback 系列(12):高级篇 · MDC 链路追踪
logback