Java 日志框架详解:SLF4J + Logback 从入门到实战

写在前面:

本文讲了日志的五种级别,和SLF4J的核心功能。利用MDC多线程跟踪日志。

再讲了Logback的常见特点。

因为SLF4J只是接口不能实现,所以最后讲了Logback的配置文件方式,这样可以自定义日志的输出方式和格式并且给出了示例方便理解和实操。

记得要引入以下依赖

复制代码
  <!-- Logback 实现(包含了 slf4j-api 的绑定) -->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.4.11</version> <!-- 建议使用较新的稳定版本 -->
        </dependency>

一、SLF4J

核心功能:

1、高效的占位符支持

SLF4J 原生支持使用 {} 作为占位符来输出动态参数。

例如:logger.info("用户 {} 登录成功", username)。

功能优势: 只有当该日志级别被启用时,SLF4J 才会进行字符串的拼接或对象的 toString() 转换。这种延迟求值机制,避免了在关闭 DEBUG 级别时依然进行无效字符串拼接所带来的 CPU 和内存开销。

2、MDC分布式链路追踪

SLF4J 提供了 MDC 功能,本质上是一个线程安全的键值对存储容器(基于 ThreadLocal)

多线程:

在多线程或线程池场景下(如使用了 TTL 包)需要额外处理,防止上下文丢失。

我们只需要通过TtlExecutors对线程池进行一次包装,无需修改任何业务代码,TTL 就会在任务提交时自动捕获主线程的 MDC 上下文,并在子线程执行前自动恢复。它帮我们屏蔽了底层繁琐的手动复制与清理逻辑,真正实现了 MDC 的无缝传递。

功能优势: 可以在请求进入系统时,将 Id 等上下文信息存入 MDC。后续该线程处理的所有日志,都可以自动带上这些标记。这在微服务和分布式系统中至关重要,能够轻松实现全链路的日志追踪与问题排查。

示例

复制代码
 private static final Logger logger = LoggerFactory.getLogger(Logger1.class);
    
    public void processRequest(String userId, String requestId) {
        // 将上下文信息放入 MDC
        MDC.put("userId", userId);
        MDC.put("requestId", requestId);
        
        try {
            // 执行业务逻辑,所有的日志都会自动包含这些上下文信息
            logger.info("开始处理请求");
            doWork();
            logger.info("请求处理完成");
        } finally {
            // 清理 MDC,防止内存泄漏
            MDC.clear();
        }
    }
    
    private void doWork() {
        // 这里打的日志会自动包含 userId 和 requestId
        logger.debug("执行具体业务逻辑");
    }

//要在xml文中的模式加入MDC中的消息才能让打印日志包含指定消息。

我这里是直接修改的全局变量LOG_PATTERN
<!-- ... existing code ... -->
    <property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %X{userId:-N/A} - %X{requestId:-N/A} - %msg%n" />
<!-- ... existing code ... -->
其他代码可以详细见三、5的完整示例

3、定义了标准的日志级别接口

核心机制:

(1)级别过滤规则

当你设置了某个日志级别后,系统只会输出该级别及以上的日志,低于该级别的会被自动过滤掉。 例如:如果将日志级别设置为 INFO,那么 INFO、WARN、ERROR 级别的日志会被输出,而 DEBUG 和 TRACE 会被直接忽略。这在生产环境中非常有用,可以屏蔽掉大量无用的调试信息,提升性能。

级别可以在xml文件中设置。

(2)异常堆栈的正确打印

在记录 ERROR 或 WARN 级别的异常时,一定要把异常对象(Exception)作为日志方法的最后一个参数传入(如ogger.error(..., e))。这样 SLF4J 才会自动打印出完整的异常堆栈信息,方便快速定位代码报错的具体行数和原因。

五种级别:

(1)TRACE

极其详细的追踪信息。通常只在开发阶段用于追踪代码的每一步执行,生产环境极少开启。

(2)DEBUG

开发调试信息。用于记录程序运行时的详细状态,帮助开发和测试人员定位问题。

(3)INFO

关键业务节点。记录系统正常运行时的重要操作,生产环境必须开启。

(4)WARN

警告信息。发生了不影响当前主流程但需要关注的异常或潜在风险。

(5)ERROR

严重错误。影响了正常业务运行的系统级错误,需要开发人员立即关注和处理。

二、Logback

核心功能:

(1)日志记录:提供灵活、高效的日志记录功能

(2)信息追踪:帮助开发者调试和监控系统运行状态

(3)性能监控:记录应用运行时的关键指标和异常信息

特点:

(1)高性能

比 log4j 更快,内存占用更少。

支持异步日志记录。

内部优化了锁机制,减少线程竞争。

(2)自动重载配置

xml文件中:scan="true" scanPeriod="60 seconds"即开启自动重载,修改配置文件后无需重启应用在被扫描的时候就会生效。

(3)灵活的输出方式

有控制台输出,文件输出两种输出方式,并且还可以自定义日志等级,异步输出,自定义文件按时间或大小分割。

(4)丰富的格式化选项

在下文三、2(1)控制台输出.输出模式中写了各种自定义转化符,可以自定义输出格式。

(5)MDC

支持在多线程环境中跟踪用户会话或请求上下文。

便于在分布式系统中追踪日志。

(6) 与 SLF4J 无缝集成

作为 SLF4J 的原生实现,提供统一的日志接口,方便切换底层实现。

三、编写Logback配置文件自定义日志输出策略

1. 定义全局变量与运行参数

作用:

(1)可以把一些重复使用的值提取成变量,方便全局调用。同时,可以控制配置文件本身的行为。

(2)设置配置文件是否自动热更新(改了配置不用重启项目)

XML 复制代码
    <!-- 定义全局变量,后面可以用 ${LOG_HOME} 来引用 -->
    <property name="LOG_HOME" value="./logs" />
    <property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n" />

2、Appender输出器

(1)控制台输出

XML 复制代码
  <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${LOG_PATTERN}</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>
输出模式(常用转换符):

这里用的:

%d{yyyy-MM-dd HH:mm:ss}

日期时间,格式为年-月-日 时:分:秒

%thread

线程名

%-5level

日志级别,左对齐,占5个字符宽度

%logger{36}

Logger 名称(通常是类名),最长36个字符

%msg

日志消息内容

%n

换行符

其他常用的:

%class

完整的类名

%X

专门用于访问 MDC的转换符

%method

方法名

%line

代码行号

%file

文件名

%L

简写的行号

%p

日志级别(同 %level)

%c

Logger 名称(同 %logger)

%t

线程名(同 %thread)

%m

消息(同 %msg)

(2)文件输出

XML 复制代码
<!-- 文件输出器 -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_HOME}/application.log</file>
        <encoder>
            <pattern>${LOG_PATTERN}</pattern>
            <charset>UTF-8</charset>
        </encoder>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_HOME}/application.%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
    </appender>

1、RollingFileAppender:滚动文件输出器,当满足特定条件时(如时间、文件大小),会自动创建新文件

**2、<file>:**指定当前正在写入的日志文件路径,完整路径是:./logs/application.log(项目根目录下的 logs 文件夹)

3、滚动策略:

(1)策略 1:基于大小和时间的混合策略(最推荐使用)

XML 复制代码
  <!-- 基于大小和时间的滚动策略 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
        <fileNamePattern>${LOG_HOME}/application.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
        <maxFileSize>50MB</maxFileSize>     <!-- 单个文件最大50MB -->
        <maxHistory>30</maxHistory>          <!-- 保留30天 -->
        <totalSizeCap>1GB</totalSizeCap>     <!-- 总大小不超过1GB -->
    </rollingPolicy>

(2)策略 2:基于大小的滚动(SizeBasedTriggeringPolicy)

XML 复制代码
 <!-- 基于大小的滚动策略 -->
    <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
        <fileNamePattern>${LOG_HOME}/application.%i.log</fileNamePattern>
        <minIndex>1</minIndex>
        <maxIndex>10</maxIndex> <!-- 最多10个文件 -->
    </rollingPolicy>
 <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
        <maxFileSize>10MB</maxFileSize> <!-- 单个文件最大10MB -->
    </triggeringPolicy>

(3)策略 3:基于时间的滚动(当前使用的)

<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">

TimeBasedRollingPolicy:基于时间的滚动策略,按天分割日志文件

**<maxHistory>**保留天数,超过时间会自动删除(项目运行的时候进行)

3、根日志记录器

XML 复制代码
   <root level="INFO">
        <appender-ref ref="CONSOLE" />
        <appender-ref ref="FILE" />
    </root>

level="INFO"默认级别

<appender-ref ref="CONSOLE" /> - 引用控制台输出器

将日志输出到控制台

可以在控制台看到 INFO 及以上级别的日志

<appender-ref ref="FILE" /> - 引用文件输出器

将日志输出到文件

日志会写入 ./logs/application.log 文件

自定义日志级别

在每个 <appender> 中添加过滤器,单独控制输出级别(加入下面这段代码在每个appender中的<encoder>前即可。

XML 复制代码
  <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>WARN</level>
        </filter>

注意:

这样实现原理是日志必须先通过 root 的检查,才能到达 Appender,进而被 Filter 过滤。所以Filter只有在级别高于root的时候才会生效。

4、异步输出

可以通过通过异步配置,可以让日志在后台线程慢慢写,不阻塞主业务。

加入以下这段AsyncAppender异步appender

XML 复制代码
 <!-- 异步文件输出器 -->
    <appender name="ASYNC_FILE" class="ch.qos.logback.classic.AsyncAppender">
        <!-- 引用实际的文件appender -->
        <appender-ref ref="FILE" />
        <!-- 队列大小,默认256 -->
        <queueSize>512</queueSize>
        <!-- 当队列剩余容量小于discardingThreshold时,会丢弃TRACE、DEBUG、INFO级别的日志 -->
        <discardingThreshold>0</discardingThreshold>
        <!-- 是否包含调用者信息,默认false -->
        <includeCallerData>false</includeCallerData>
    </appender>

再修改root logger

XML 复制代码
    <!-- 旧根日志记录器 -->
    <root level="INFO">
        <appender-ref ref="CONSOLE" />
        <appender-ref ref="FILE" />
    </root>

    <!-- 新根日志记录器 -->
    <root level="INFO">
        <appender-ref ref="CONSOLE" />
        <appender-ref ref="ASYNC_FILE" />
    </root>

5、完整示例

logback.xml

XML 复制代码
<!-- scan="true" 表示配置文件修改后会自动重新加载,scanPeriod设置检查间隔 -->
<configuration scan="true" scanPeriod="60 seconds">

    <!-- 定义全局变量,后面可以用 ${LOG_HOME} 来引用 -->
    <property name="LOG_HOME" value="./logs" />
    <property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n" />

    <!-- 控制台输出器 -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${LOG_PATTERN}</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>

    <!-- 文件输出器 -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_HOME}/application.log</file>
        <encoder>
            <pattern>${LOG_PATTERN}</pattern>
            <charset>UTF-8</charset>
        </encoder>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_HOME}/application.%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
    </appender>

    <!-- 异步文件输出器 -->
    <appender name="ASYNC_FILE" class="ch.qos.logback.classic.AsyncAppender">
        <!-- 引用实际的文件appender -->
        <appender-ref ref="FILE" />
        <!-- 队列大小,默认256 -->
        <queueSize>512</queueSize>
        <!-- 当队列剩余容量小于discardingThreshold时,会丢弃TRACE、DEBUG、INFO级别的日志 -->
        <discardingThreshold>0</discardingThreshold>
        <!-- 是否包含调用者信息,默认false -->
        <includeCallerData>false</includeCallerData>
    </appender>

    <!-- 根日志记录器 -->
    <root level="INFO">
        <appender-ref ref="CONSOLE" />
        <appender-ref ref="ASYNC_FILE" />
    </root>

</configuration>

主函数

可以看到无Debug输出

java 复制代码
import java.util.concurrent.*;

import com.alibaba.ttl.TransmittableThreadLocal;
import com.alibaba.ttl.threadpool.TtlExecutors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Main {

    private static final Logger logger=LoggerFactory.getLogger(Main.class);
    public static void main(String[] args)  {
        logger.info("这是一条INFO日志");
        logger.debug("这是一条DEBUG日志");
        logger.error("这是一条ERROR日志");
    }
}

//输出
2026-05-27 00:49:04 [main] INFO  Main - 这是一条INFO日志
2026-05-27 00:49:04 [main] ERROR Main - 这是一条ERROR日志

日志文件

6、无logback配置文件的初始效果

pom.xml 中引入了 logback-classic 依赖,Logback 会启动并使用其内置的默认配置

控制台输出:

只会在控制台输出日志

默认日志级别是 DEBUG

不会有文件输出不能持久化日志。

下面是默认的格式输出


到这里本篇结束!

相关推荐
ylscode1 小时前
黑客利用 GHOSTYNETWORKS 和 OMEGATECH 托管 JS 恶意软件基础设施
开发语言·安全·php·安全威胁分析
爱吃生蚝的于勒1 小时前
QT开发第二章——信号和槽
c语言·开发语言·c++·qt
xcLeigh1 小时前
Python入门:Python3 operator模块全面学习教程
开发语言·python·学习·教程·python3·operator
Dest1ny-安全1 小时前
2026最新CTF知识库:12大Web漏洞深度文章+1156篇历年大赛WP+50+脚本+Payload速查 +AI/RAG离线在线知识库
java·学习·安全·web安全·servlet
404号扳手1 小时前
Java 基础知识(六)
java·后端
大叔带刺1 小时前
使用python创建自己的专属星座签名APP:Name2Constell
开发语言·python·pygame
z落落2 小时前
C# 类与对象、字段、静态与非静态+四大访问修饰符
开发语言·c#
思麟呀2 小时前
C++工业级日志项目(八)最终上层接口
开发语言·c++
石山代码2 小时前
如何在 C++ 中实现多态?
开发语言·c++