学完这篇,你能独立搭起一套「代码只依赖 SLF4J、日志输出由 Logback 控制」的日志体系,包括依赖排查、配置文件骨架、分级策略和生产环境的滚动与异步设置。
第一步:把依赖关系摆正
SLF4J 是门面(只提供接口),Logback 是实现(真正写日志)。业务代码里 import 的永远只有 org.slf4j.Logger,这样以后换实现不用改一行业务代码。
Maven 里加两个依赖就够了:
<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>
logback-classic 会传递带入 logback-core,不用单独声明。
注意:SLF4J 2.x 必须配 Logback 1.4/1.5,SLF4J 1.7 只能配 Logback 1.2.x。版本错配的典型症状是启动就报 NoSuchMethodError 或 StaticLoggerBinder 找不到。
第二步:代码里只用 SLF4J 的 API
public class UserService {
private static final Logger log = LoggerFactory.getLogger(UserService.class);
public void login(Long userId, String ip) {
log.info("用户 {} 登录,IP={}", userId, ip);
try {
// 业务
} catch (Exception e) {
log.error("用户 {} 登录失败", userId, e);
}
}
}
两个要点:Logger 声明成 static final,别每次 new;用 {} 占位符,别用字符串 + 拼接——占位符在日志级别不输出时不会做字符串拼接,省性能。
注意:log.error("失败", e) 里最后一个参数是 Throwable 时,Logback 会自动打印完整堆栈,不要写成 log.error(e.getMessage()),那样栈就丢了。
第三步:写 logback.xml 骨架
文件放 src/main/resources/logback.xml,Logback 启动时自动加载。结构就三层:appender(往哪写)、logger(谁写的、什么级别)、root(兜底)。
<configuration>
<property name="LOG_HOME" value="./logs"/>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/app.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/app.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>50MB</maxFileSize>
<maxHistory>30</maxHistory>
<totalSizeCap>2GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<logger name="com.example" level="DEBUG"/>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
</root>
</configuration>
SizeAndTimeBasedRollingPolicy 是生产必备:按天切、单文件超 50MB 再切、保留 30 天、总量封顶 2GB,防止日志把磁盘吃满。
注意:Windows 控制台默认 GBK,<charset>UTF-8</charset> 不加可能出现中文乱码。另外 logback.xml 会被 Logback 直接加载,如果用了 Spring Boot 的 <springProfile> 标签,文件名必须改成 logback-spring.xml,否则标签不生效。
第四步:按包分级,把噪音压下去
root 定 INFO,自己业务包开 DEBUG,框架包按需压:
<logger name="com.example" level="DEBUG"/>
<logger name="org.apache.ibatis" level="DEBUG"/>
<logger name="org.springframework" level="WARN"/>
级别顺序是 TRACE < DEBUG < INFO < WARN < ERROR,子 logger 会继承 root 的 appender。
注意:如果给某个 logger 单独挂了 appender-ref,又不想让它再继承 root 的输出,要加 <additivity>false</additivity>,否则同一条日志会打两遍。
第五步:生产环境补两件事
一是异步,避免磁盘 IO 拖慢请求:
<appender name="ASYNC_FILE" class="ch.qos.logback.classic.AsyncAppender">
<queueSize>1024</queueSize>
<discardingThreshold>0</discardingThreshold>
<appender-ref ref="FILE"/>
</appender>
discardingThreshold 设为 0 表示队列满时不丢任何级别的日志(默认会丢 TRACE/DEBUG/INFO)。
二是临时调级别不用重启。加 scan="true" scanPeriod="30 seconds" 到 <configuration> 上,改完配置 30 秒内自动生效;Spring Boot 项目也可以直接打 Actuator 的 /actuator/loggers 端点动态改。
注意:项目里如果同时存在 log4j、commons-logging 等日志实现,启动会提示 Class path contains multiple SLF4J bindings。用 mvn dependency:tree 找出多余的实现并 <exclusions> 掉,否则日志行为不可控。
小结
- SLF4J 只管接口,Logback 管输出,业务代码永远只 import
org.slf4j。
- 版本要对齐:SLF4J 2.x + Logback 1.5.x,SLF4J 1.7 + Logback 1.2.x。
- 用
{} 占位符,Throwable 放最后一个参数,别丢堆栈。
logback.xml 三件套:appender / logger / root,文件用 SizeAndTimeBasedRollingPolicy 防撑爆磁盘。
- 分级靠
logger name 精确控制,重复输出用 additivity=false 解决。
- 生产加 AsyncAppender,
discardingThreshold=0,配合 scan 或 Actuator 动态调级别。