SLF4J

SLF4J(Simple Logging Facade for Java)是 Java 日志的门面抽象层,本身不提供日志实现,而是统一了日志调用 API,底层实现可替换为 Logback、Log4j2 等。

Spring Boot 默认使用 SLF4J + Logback 组合,引入 spring-boot-starter 自动生效。日志配置详见 Spring Boot 日志

核心设计

你的代码(只调用 SLF4J API)
  ↓
SLF4J 桥接层(slf4j-api)
  ↓ 运行时自动绑定
具体实现(Logback / Log4j2 / java.util.logging)

代码只依赖 SLF4J 接口,底层实现可随时替换,不影响业务代码。

依赖配置

Spring Boot(自动包含,无需手动添加)

<!-- spring-boot-starter 已包含 SLF4J + Logback -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>

切换为 Log4j2

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>

非 Spring 项目(手动引入)

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>2.0.13</version>
</dependency>
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.5.6</version>
</dependency>

基本使用

创建 Logger

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
public class UserService {
    private static final Logger log = LoggerFactory.getLogger(UserService.class);
}

Lombok @Slf4j(推荐)

import lombok.extern.slf4j.Slf4j;
 
@Slf4j
@Service
public class UserService {
    public void createUser(String name) {
        log.info("Creating user: {}", name);
    }
}

日志级别

从低到高:TRACE < DEBUG < INFO < WARN < ERROR

设置级别后,低于该级别的日志不输出(设为 INFO 则 TRACE/DEBUG 不输出)。

log.trace("最细粒度,调试循环内部逻辑");
log.debug("调试信息,开发环境输出,生产环境关闭");
log.info("关键业务节点:用户登录、订单创建");
log.warn("潜在问题:配置缺失、重试发生");
log.error("需要处理的错误,通常附带异常");
级别典型场景
TRACE方法进出、循环迭代,生产不开启
DEBUGSQL 语句、参数值、中间计算结果
INFO服务启动/停止、用户操作、定时任务
WARN降级逻辑触发、依赖服务慢、配置不完整
ERROR未捕获异常、数据库操作失败、外部接口故障

参数化日志(重要)

使用 {} 占位符,避免字符串拼接——只有日志真正输出时才格式化:

// 错误:无论级别是否输出,都执行拼接
log.debug("User: " + user.getName() + ", Age: " + user.getAge());
 
// 正确:懒求值,DEBUG 关闭时不执行拼接
log.debug("User: {}, Age: {}", user.getName(), user.getAge());

记录异常时,异常对象放最后一个参数(不加 {}),SLF4J 自动输出完整堆栈:

try {
    userRepository.save(user);
} catch (Exception e) {
    log.error("Failed to save user: {}", user.getId(), e);
}

MDC(Mapped Diagnostic Context)

MDC 是线程局部的 Map,用于在日志中附加请求级上下文(traceId、userId)。在过滤器中设置一次,整个请求链路的日志自动携带。

// 请求进入时设置
MDC.put("traceId", UUID.randomUUID().toString());
MDC.put("userId", String.valueOf(currentUserId));
 
// 业务代码正常打日志,无需手动传递
log.info("Processing order: {}", orderId);
// 输出:[traceId=abc-123] [userId=42] Processing order: 1001
 
// 请求结束时清理(防止线程池复用时数据泄露)
MDC.clear();

logback.xml 中通过 %X{key} 引用 MDC 变量:

<pattern>%d{HH:mm:ss} [%X{traceId}] [%X{userId}] %-5level %logger - %msg%n</pattern>

通过 OncePerRequestFilter 统一管理 MDC:

@Component
public class MdcFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain)
            throws ServletException, IOException {
        String traceId = Optional.ofNullable(req.getHeader("X-Trace-Id"))
            .orElse(UUID.randomUUID().toString());
        MDC.put("traceId", traceId);
        try {
            chain.doFilter(req, res);
        } finally {
            MDC.clear();
        }
    }
}

日志桥接(Bridging)

第三方库可能使用 log4j 1.x、commons-logging 等框架,SLF4J 提供桥接器将其调用重定向到 SLF4J 统一处理:

<!-- 将 log4j 1.x 桥接到 SLF4J -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>log4j-over-slf4j</artifactId>
</dependency>
 
<!-- 将 commons-logging 桥接到 SLF4J -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jcl-over-slf4j</artifactId>
</dependency>
 
<!-- 将 java.util.logging 桥接到 SLF4J -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jul-to-slf4j</artifactId>
</dependency>

桥接时需同时排除被桥接框架的原始依赖,避免循环绑定导致 StackOverflow。

SLF4J vs 其他日志框架

框架类型说明
SLF4J门面(API)只定义接口,代码中推荐依赖此层
Logback实现Spring Boot 默认,SLF4J 原生支持
Log4j2实现性能优秀,支持异步日志,适合高吞吐场景
Log4j 1.x实现已停止维护,存在安全漏洞,不应使用
java.util.logging实现JDK 内置,功能弱,一般不用

相关链接