Java日志系统
目录
日志系统
前言
-Dlogback.configurationFile=D:\file\IDEA_File\logDemo\config\logback-main.xml
JAVA日志框架的使用_java logger-CSDN博客
(7 封私信) Java 日志管理的黄金组合: SLF4J+Logback - 知乎

详细历史背景
1. 混沌初期(2001年之前)
- 各自为战:每个项目、每个开发者都有自己的日志方式。
System.out.println:最简单的调试输出。- Log4j 1.x:第一个真正意义上的专业日志框架,由Ceki Gülcü开发,后来捐赠给Apache。它功能强大,成为事实标准。
2. JDK的尝试(2002年)
- Sun公司在JDK 1.4中引入了
java.util.logging,希望统一日志标准。 - 但问题来了:
- JUL设计相对简陋,性能不如Log4j
- 此时Log4j已经广泛流行,很多项目不愿意迁移
- 出现了Log4j vs JUL的阵营分裂
3. 门面模式的诞生(2005-2006年)
为什么需要门面?
想象一下这个场景:
- 你的项目使用了Library A,它用Log4j写日志
- 你又引入了Library B,它用JUL写日志
- 现在你的应用要配置两套日志系统,输出格式不统一,难以管理
解决方案:门面模式
- JCL登场:Apache推出了Commons Logging,这是第一个主流的日志门面。
- 你的代码面向JCL API
- JCL在运行时通过类加载器机制自动发现底层的Log4j或JUL
- 但JCL有著名的类加载器问题,在复杂的Web容器中经常出问题
- SLF4J回应:Log4j的作者Ceki Gülcü对JCL不满意,开发了SLF4J
- 解决JCL的类加载器问题
- 采用静态绑定,更可靠
- 提供更优雅的API(如
{}占位符,避免字符串拼接性能开销)
4. 现代阶段
- Logback:Ceki Gülcü想彻底改进Log4j,但不愿破坏兼容性,于是重写了Logback,原生实现SLF4J API
- Log4j 2:Apache社区看到Logback的成功,决定重写Log4j,吸取了Logback的优点,但保持了独立API
Java主要日志框架历史沿革总结
| 名称 | 出现时间 | 制作人/组织 | 类型 | 设计目标/特点 | 现状评价 |
|---|---|---|---|---|---|
| Log4j 1.x | 1999-2001 | Ceki Gülcü (后捐赠给Apache) | 日志框架 | 第一个专业的Java日志框架,功能完整强大 | 已淘汰,存在性能问题和内存泄漏 |
| JUL (java.util.logging) | 2002 (JDK 1.4) | Sun Microsystems | 日志框架 | JDK内置,希望统一日志标准 | 仍在使用,但功能性能一般,多用于简单项目 |
| JCL (Apache Commons Logging) | 2003-2005 | Apache | 日志门面 | 第一个主流日志门面,解决多日志框架共存问题 | 逐渐被替代,有著名的类加载器问题 |
| SLF4J | 2005-2006 | Ceki Gülcü | 日志门面 | 解决JCL的缺陷,提供更优雅的API和可靠绑定 | 当前主流,事实上的标准门面 |
| Logback | 2006 | Ceki Gülcü | 日志框架 | Log4j的精神继任者,原生实现SLF4J API | 当前主流,性能优秀,与SLF4J完美配合 |
| Log4j 2 | 2012-2014 | Apache | 日志框架 | 完全重写,吸取Logback优点,支持异步和插件 | 当前主流,高性能,功能最丰富 |
重要桥接组件
| 名称 | 出现时间 | 作用 | 使用场景 |
|---|---|---|---|
| log4j-over-slf4j | 2006左右 | 将Log4j 1.x API调用桥接到SLF4J | 迁移老旧项目,统一日志门面 |
| jcl-over-slf4j | 2006左右 | 将Commons Logging调用桥接到SLF4J | 解决JCL的类加载器问题 |
| jul-to-slf4j | 2006左右 | 将java.util.logging调用桥接到SLF4J | 将JUL日志统一到SLF4J体系 |
| log4j-slf4j-impl | 2012左右 | SLF4J到Log4j 2的绑定器 | 在使用Log4j 2作为实现时使用 |
| log4j-to-slf4j | 2012左右 | 将Log4j 2 API调用桥接到SLF4J | 需要将Log4j2 API调用重定向到SLF4J时 |
关键时间节点图示

总结
- 先驱时代:Log4j 1 → JUL(各自为战)
- 门面诞生:JCL → SLF4J(解决兼容性问题)
- 现代时代:Logback → Log4j 2(性能与功能的竞争)
当前推荐组合:**SLF4J(门面) + Log4j 2/Logback(实现)**
一个日志框架的使用
SLF4J(门面) + Logback(实现)
项目依赖 (Maven)
<!-- java 11 -->
<dependencies>
<!-- SLF4J 门面 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.9</version>
</dependency>
<!-- Logback 实现 (包含SLF4J绑定) -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.4.11</version>
</dependency>
</dependencies>
<!-- java 8 -->
<dependencies>
<!-- SLF4J 门面 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.36</version>
</dependency>
<!-- Logback 实现 (包含SLF4J绑定) -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.11</version>
</dependency>
</dependencies>
配置文件:src/main/resources/logback.xml
- 默认找
src/main/resources/下的logback.xml - 也可以配置指定文件,优先级比默认的高:
-Dlogback.configurationFile=D:\file\IDEA_File\mu4_mu3h5\mu4-mu3h5\config\config-logic\logback-main.xml
默认查找顺序
看有没有配置
-Dlogback.configurationFile=,优先用该配置类路径中查找以下文件(按优先级顺序):
- `logback.groovy``
- ``logback-test.xml`
- logback.xml`
如果以上都找不到,Logback 会使用:
ch.qos.logback.classic.BasicConfigurator- 这个基础配置器会设置一个最小化配置,将日志输出到控制台
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">
<!-- 控制台输出 -->
<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>
</encoder>
</appender>
<!-- <!– 文件输出 - 简化版本 –>-->
<!-- <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">-->
<!-- <file>logs/myapp.log</file>-->
<!-- <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">-->
<!-- <fileNamePattern>logs/myapp.%d{yyyy-MM-dd}.log</fileNamePattern>-->
<!-- <maxHistory>30</maxHistory>-->
<!-- </rollingPolicy>-->
<!-- <encoder>-->
<!-- <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>-->
<!-- </encoder>-->
<!-- </appender>-->
<!-- 文件输出 - 使用 SizeAndTimeBasedRollingPolicy -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/myapp.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>logs/myapp.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxFileSize>100MB</maxFileSize>
<maxHistory>30</maxHistory>
<totalSizeCap>3GB</totalSizeCap>
</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>
<!-- 特定包日志级别 -->
<!-- <logger name="com.example.service" level="INFO"/>-->
</configuration>
控制台输出:
<?xml version="1.0" encoding="UTF-8" ?>
<configuration scan="true" scanPeriod="5 seconds" debug="true">
<!--LevelFilter: 级别 == 配置级别-->
<!--ThresholdFilter: 级别 >= 配置级别-->
<!-- Appender内部:过滤器是顺序的(链式处理) -->
<!-- Appender之间:过滤器是并行的(独立处理) -->
<!-- 在单个Appender内部,过滤器确实是按配置顺序执行的! -->
<!-- 控制台 INFO-->
<appender name="consoleInfoAppender" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>[%d{yyyy-MM-dd HH:mm:ss} [%thread] %.-5level]%C{0}:%L %msg%n</pattern>
</encoder>
<target>System.out</target>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 控制台 DEBUG-->
<appender name="consoleDebugAppender" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>[%d{yyyy-MM-dd HH:mm:ss} [%thread] %.-5level]%C{0}:%L %msg%n</pattern>
</encoder>
<target>System.out</target>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>DEBUG</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 控制台 WARN ERROR-->
<appender name="consoleErrAppender" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>[%d{yyyy-MM-dd HH:mm:ss} [%thread] %.-5level]%C{0}:%L %msg%n</pattern>
</encoder>
<target>System.err</target>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>WARN</level>
</filter>
</appender>
<!-- 关键:Root Logger 必须配置为DEBUG级别 如果配置INFO,那么consoleDebugAppender就被过滤掉了-->
<root level="DEBUG">
<appender-ref ref="consoleInfoAppender" />
<appender-ref ref="consoleDebugAppender" />
<appender-ref ref="consoleErrAppender" />
</root>
</configuration>
Java 代码
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LogbackExample {
// 获取Logger实例
private static final Logger logger = LoggerFactory.getLogger(LogbackExample.class);
public static void main(String[] args) {
logger.info("应用程序启动...");
try {
processData("测试数据");
simulateError();
} catch (Exception e) {
logger.error("处理过程中发生错误", e);
}
logger.debug("这是一条调试信息");
logger.warn("这是一条警告信息");
logger.info("应用程序结束");
}
private static void processData(String data) {
logger.info("开始处理数据: {}", data); // 使用占位符,避免字符串拼接
// 模拟业务逻辑
logger.debug("数据处理中...");
}
private static void simulateError() {
try {
int result = 10 / 0;
} catch (ArithmeticException e) {
logger.error("除零错误发生", e);
throw new RuntimeException("计算失败", e);
}
}
}
SLF4J(门面) + Log4j 2(实现)
项目依赖 (Maven)
<dependencies>
<!-- SLF4J 门面 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.9</version>
</dependency>
<!-- Log4j2 的 SLF4J 绑定器 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j2-impl</artifactId>
<version>2.22.0</version>
</dependency>
<!-- Log4j2 核心 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.22.0</version>
</dependency>
</dependencies>
配置文件:src/main/resources/log4j2.xml
默认查找顺序
系统属性指定(优先级最高):-Dlog4j.configurationFile=path/to/log4j2.xml
类路径中的配置文件(按优先级从高到低):
log4j2-test.properties- 测试环境 properties 格式log4j2-test.yaml或log4j2-test.yml- 测试环境 YAML 格式log4j2-test.json或log4j2-test.jsn- 测试环境 JSON 格式log4j2-test.xml- 测试环境 XML 格式log4j2.properties- 生产环境 properties 格式log4j2.yaml或log4j2.yml- 生产环境 YAML 格式log4j2.json或log4j2.jsn- 生产环境 JSON 格式log4j2.xml- 生产环境 XML 格式(你提到的这个)使用 默认配置 - 输出到控制台的简单配置
- 主要类:
org.apache.logging.log4j.core.config.DefaultConfiguration
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<!-- 控制台输出 -->
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
<!-- 文件输出 -->
<RollingFile name="File" fileName="logs/app.log"
filePattern="logs/app-%d{yyyy-MM-dd}-%i.log">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
<Policies>
<TimeBasedTriggeringPolicy />
<SizeBasedTriggeringPolicy size="100 MB"/>
</Policies>
<DefaultRolloverStrategy max="30"/>
</RollingFile>
<!-- 异步日志Appender -->
<Async name="AsyncFile">
<AppenderRef ref="File"/>
</Async>
</Appenders>
<Loggers>
<!-- 特定包日志配置 -->
<Logger name="com.example.service" level="info" additivity="false">
<AppenderRef ref="Console"/>
</Logger>
<!-- 根日志配置 -->
<Root level="debug">
<AppenderRef ref="Console"/>
<AppenderRef ref="AsyncFile"/>
</Root>
</Loggers>
</Configuration>
Java 代码
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Log4j2Example {
// 获取Logger实例 - 代码与Logback完全相同!
private static final Logger logger = LoggerFactory.getLogger(Log4j2Example.class);
public static void main(String[] args) {
logger.info("应用程序启动 - 使用Log4j2后端");
demonstrateLogging();
logger.info("应用程序结束");
}
private static void demonstrateLogging() {
// 使用参数化日志,避免不必要的字符串拼接
String user = "张三";
int age = 25;
logger.info("用户信息: 姓名={}, 年龄={}", user, age);
// 不同日志级别
logger.trace("这是一条TRACE级别日志");
logger.debug("这是一条DEBUG级别日志");
logger.warn("这是一条WARN级别日志");
// 记录异常
try {
performRiskyOperation();
} catch (IllegalStateException e) {
logger.error("执行危险操作时发生错误, 用户={}", user, e);
}
// 性能监控示例
long startTime = System.currentTimeMillis();
try {
Thread.sleep(100); // 模拟业务操作
logger.debug("业务操作执行完成");
} catch (InterruptedException e) {
logger.warn("操作被中断", e);
Thread.currentThread().interrupt();
}
long endTime = System.currentTimeMillis();
logger.info("操作执行时间: {}ms", (endTime - startTime));
}
private static void performRiskyOperation() {
logger.debug("开始执行危险操作");
if (Math.random() > 0.5) {
throw new IllegalStateException("随机失败模拟");
}
logger.debug("危险操作执行成功");
}
}
关键对比总结
| 方面 | SLF4J + Logback | SLF4J + Log4j 2 |
|---|---|---|
| 依赖数量 | 2个 (slf4j-api + logback-classic) |
3个 (slf4j-api + log4j-slf4j2-impl + log4j-core) |
| 配置语法 | Logback自有语法 | Log4j2 XML语法 |
| 代码层面 | 完全一致 (都使用SLF4J API) | 完全一致 (都使用SLF4J API) |
| 异步日志 | 需要额外配置 | 原生支持,配置简单 |
| 性能特点 | 优秀 | 极佳,特别是在异步模式下 |
重要提示
- 代码完全兼容:两种实现的Java代码完全一样,这正是使用日志门面的价值所在!
- 切换简单:【SLF4J + Logback】和【SLF4J + Log4j 2】相互切换,只需更改依赖和xml配置文件,无需修改任何Java代码
- 配置文件位置:确保配置文件放在
src/main/resources/目录下 - 依赖冲突:注意不要同时引入多个绑定器(如同时有logback-classic和log4j-slf4j2-impl)
两种组合都是生产环境中的优秀选择,根据具体需求(性能要求、功能特性、团队熟悉度)来选择即可。
日志文件详细解析
Logback日志配置详解_logback 配置文件-CSDN博客
- 再不懂得问deepseek!
浙公网安备 33010602011771号