news 2026/8/23 8:29:29

【框架】日志-SLF4J+Logback

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【框架】日志-SLF4J+Logback

文章目录

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

一、SLF4J和Logback分别是什么?

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

SLF4J = 规定“怎么记日志”的接口/门面
Logback = 真正负责“把日志记下来”的实现

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

1. SLF4J 是什么?

SLF4J 全称是Simple Logging Facade for Java

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

比如你写:

importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;Loggerlog=LoggerFactory.getLogger(UserService.class);log.info("用户登录");

这里的:

Loggerlog

就是SLF4J 提供的 Logger 接口

你调用:

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

实际上是在说:

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

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

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


2. Logback 是什么?

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

它负责具体执行:

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

例如:

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

最终可能被 Logback 输出成:

2026-08-19 15:20:31 INFO UserService - 用户登录

并且可以输出到:

控制台 ↓ ConsoleAppender

或者:

日志文件 ↓ FileAppender

甚至按日期生成:

logs/ ├── app.2026-08-18.log ├── app.2026-08-19.log └── app.log

这些才是 Logback 干的活。


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

可以画成:

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

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

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


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

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

ch.qos.logback.classic.Loggerlog;

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

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

而使用:

org.slf4j.Logger

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

业务代码 ↓ SLF4J Logger ↓ Logback

以后换实现:

业务代码 ↓ SLF4J Logger ↓ Log4j2

业务代码基本不用动。

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

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


5. 一句话区分

SLF4JLogback
是什么日志门面/抽象日志实现
主要作用定义日志 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项目,需引入依赖如下:

<!-- 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 version="1.0" encoding="UTF-8"?><configuration><appendername="CONSOLE"class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern></encoder></appender><appendername="FILE"class="ch.qos.logback.core.rolling.RollingFileAppender"><file>logs/app.log</file><rollingPolicyclass="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><rootlevel="INFO"><appender-refref="CONSOLE"/><appender-refref="FILE"/></root></configuration>

3、使用日志

新建Logger对象:

Loggerlog=LoggerFactory.getLogger(ClientApplication.class);

打印不同级别的日志:

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

例子如下:

publicclassClientApplicationextendsApplication{privatestaticfinalLoggerlog=LoggerFactory.getLogger(ClientApplication.class);publicstaticvoidmain(String[]args){launch(args);}@Overridepublicvoidstart(Stagestage){log.info("===info:开始组装machine注册信息===");log.debug("===debug:开始组装machine注册信息===");log.warn("===warn:开始组装machine注册信息===");log.error("===error:开始组装machine注册信息===");MachineRegisterDTOdto=newMachineRegisterDTO();dto.setMachineName(MachineInfoUtil.getMachineName());dto.setIp(MachineInfoUtil.getIp());dto.setMac(MachineInfoUtil.getMac());dto.setStatus("ONLINE");register(dto);stage.show();}}

日志打印效果:

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

三、拓展:使用注解

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

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

你之前理解的:

privatestaticfinalLoggerlog=LoggerFactory.getLogger(ClientApplication.class);

和:

@Slf4jpublicclassClientApplication{}

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


1.@Slf4j是谁提供的?

Lombok

importlombok.extern.slf4j.Slf4j;

注意这里:

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

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


2. 它到底生成了什么?

你写:

@Slf4jpublicclassUserService{publicvoidlogin(){log.info("用户登录");}}

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

publicclassUserService{privatestaticfinalLoggerlog=LoggerFactory.getLogger(UserService.class);publicvoidlogin(){log.info("用户登录");}}

所以:

@Slf4j

最重要的作用其实就是:

帮你生成log这个 Logger 对象。


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

这里是理解@Slf4j的关键。

不是:

程序启动 ↓ 发现 @Slf4j ↓ 动态生成 Logger

而主要是:

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

也就是说:

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


4. Lombok 到底生成了什么?

例如:

@Slf4jpublicclassUserService{publicvoidlogin(){log.info("用户登录");}}

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

privatestaticfinalorg.slf4j.Loggerlog=org.slf4j.LoggerFactory.getLogger(UserService.class);

然后你的:

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

就有东西可以调用了。

所以整个关系变成:

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

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

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

这是非常重要的一点。

例如:

@Slf4jpublicclassUserService{publicvoidlogin(){log.info("用户登录");}}

Lombok生成的大概是:

privatestaticfinalLoggerlog=LoggerFactory.getLogger(UserService.class);

这里的:

LoggerLoggerFactory

来自:

SLF4J

而不是 Logback。

然后运行的时候:

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

因此:

Lombok

负责:

帮你生成 Logger 代码

SLF4J

负责:

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

Logback

负责:

真正实现日志输出


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

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

privatestaticfinalLoggerlog=LoggerFactory.getLogger(UserService.class);

所以:

@Slf4jpublicclassUserService{publicvoidlogin(){log.info("登录");}}

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

publicclassUserService{privatestaticfinalLoggerlog=LoggerFactory.getLogger(UserService.class);publicvoidlogin(){log.info("登录");}}

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


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

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

你需要:

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

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

比如:

第一种:Lombok没配置

importlombok.extern.slf4j.Slf4j;

都找不到。


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

可能出现:

Cannot resolve symbol 'log'

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


第三种:没有 SLF4J

Lombok生成的是类似:

LoggerFactory.getLogger(...)

它还是需要 SLF4J。


第四种:没有日志实现

例如 SLF4J 存在:

你的代码 ↓ SLF4J

但没有:

Logback

之类的实现。

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

No SLF4J providers were found

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


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

在 IDEA 里找到:

@Slf4jpublicclassUserService{publicvoidtest(){log.info("hello");}}

然后使用 IDEA 的:

View → Show Bytecode

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

你最终会看到类似:

privatestaticfinalLoggerlog=LoggerFactory.getLogger(UserService.class);

这就是@Slf4j的核心秘密。


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

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

编译阶段 │ ▼ @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负责“真正干活”。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/23 8:29:14

产品说“用户不会这么用“,我的告警群先笑了

评审会上有一幕&#xff0c;做过复杂需求的人都见过。方案推到墙角了&#xff0c;谁都没有更好的法子&#xff0c;我们把用户某些操作可能引起的问题摆上桌&#xff0c;得到的回复是五个字&#xff1a; “用户不会这么用。” 会议室一般会安静两秒。不是被说服了&#xff0c;是…

作者头像 李华
网站建设 2026/8/23 8:28:38

Java main class搞不懂?新手看完直接开窍,别再懵了

不少刚入门的新手看过主题”C#能实现何种功能“以后, 满怀兴致地开启了, 设立了一项工程,然则一望见一大批文档、一连串的编码, 瞬间就愣住了: 这些文档究竟是用于干嘛的? ”using“究竟代表着什么含义? ”代表的是什么? “ void Main ”指代的又具体是什么?别着急, 今儿我…

作者头像 李华
网站建设 2026/8/23 8:26:10

经纬度到平面坐标转换:割草机路径规划中的坐标投影实战

1. 项目概述&#xff1a;当割草机遇上经纬度 最近在做一个挺有意思的项目&#xff0c;核心是把一个看似简单的物理问题——割草机的路径规划&#xff0c;和地理信息系统&#xff08;GIS&#xff09;里最基础的经纬度坐标转换给结合起来了。听起来是不是有点跨界&#xff1f;其实…

作者头像 李华
网站建设 2026/8/23 8:23:00

读懂数字化转型 | 选、育、用、留:数字化人才体系的“四步棋”

很多企业数字化转型卡在最后一公里&#xff1a;系统上线完备&#xff0c;却没人会用、没人愿用。本文点明数字化转型的核心瓶颈从来不是技术&#xff0c;而是人才与管理者心智模式。拆解数字化三层人才梯队、人才选育用留四步完整体系&#xff0c;详解运营体系从人治到数据驱动…

作者头像 李华
网站建设 2026/8/23 8:22:16

双参数理论:动态语义与相位敏感如何革新NLP与LLM理解

这次我们来看一个关于语言认知机制的研究项目。这个项目不是传统的AI模型或工具&#xff0c;而是一项探索语言理解底层原理的学术研究&#xff0c;它提出了一个新颖的“双参数”理论框架。对于从事自然语言处理、认知科学、大语言模型&#xff08;LLM&#xff09;可解释性研究&…

作者头像 李华
网站建设 2026/8/23 8:21:06

离散型随机变量解题全攻略:从概念到实战四步法

1. 这篇文章真正要解决的问题如果你正在学习A-Level数学&#xff0c;尤其是S1&#xff08;统计1&#xff09;模块&#xff0c;那么“离散型随机变量”这个概念绝对是你绕不开的核心。很多同学在初次接触时&#xff0c;会陷入一个误区&#xff1a;以为只要会套用公式E(X) ΣxP(…

作者头像 李华