本文目录导读:

我将为您提供一个完整的Java日志实操案例,涵盖主流日志框架的使用。
项目结构
java-logging-demo/
├── pom.xml
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/example/logging/
│ │ │ ├── MainApp.java
│ │ │ └── service/
│ │ │ └── UserService.java
│ │ └── resources/
│ │ ├── log4j2.xml
│ │ ├── logback.xml
│ │ └── logging.properties
│ └── test/
│ └── java/
│ └── com/example/logging/
│ └── LogTest.java
Maven依赖配置(pom.xml)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>java-logging-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- SLF4J核心API -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.9</version>
</dependency>
<!-- Logback实现 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.4.11</version>
</dependency>
<!-- Log4j2实现 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.20.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j2-impl</artifactId>
<version>2.20.0</version>
</dependency>
<!-- 桥接包(将其他日志框架转向SLF4J) -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
<version>2.0.9</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<version>2.0.9</version>
</dependency>
<!-- Lombok(可选,用于简化日志声明) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
Logback配置(logback.xml)
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="30 seconds">
<!-- 定义属性 -->
<property name="LOG_PATH" value="logs"/>
<property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%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_PATH}/application.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 每天滚动 -->
<fileNamePattern>${LOG_PATH}/application.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!-- 保留30天 -->
<maxHistory>30</maxHistory>
<!-- 每个日志文件最大10MB -->
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>10MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
<pattern>${LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- 错误日志单独输出 -->
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/error.log</file>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/error.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>90</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- 异步日志(提升性能) -->
<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
<!-- 不丢失日志,默认如果队列满会丢弃 -->
<discardingThreshold>0</discardingThreshold>
<!-- 队列大小 -->
<queueSize>1024</queueSize>
<appender-ref ref="FILE"/>
</appender>
<!-- 设置日志级别 -->
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="ASYNC"/>
<appender-ref ref="ERROR_FILE"/>
</root>
<!-- 特定包设置不同的日志级别 -->
<logger name="com.example.logging" level="DEBUG"/>
<logger name="org.springframework" level="WARN"/>
</configuration>
Log4j2配置(log4j2.xml)
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN" monitorInterval="30">
<Properties>
<Property name="LOG_PATH">logs</Property>
<Property name="LOG_PATTERN">%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %c{1.} - %msg%n</Property>
</Properties>
<Appenders>
<!-- 控制台输出 -->
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="${LOG_PATTERN}"/>
</Console>
<!-- 滚动文件输出 -->
<RollingFile name="RollingFile"
fileName="${LOG_PATH}/app.log"
filePattern="${LOG_PATH}/app-%d{yyyy-MM-dd}-%i.log.gz">
<PatternLayout pattern="${LOG_PATTERN}"/>
<Policies>
<TimeBasedTriggeringPolicy />
<SizeBasedTriggeringPolicy size="10 MB"/>
</Policies>
<DefaultRolloverStrategy max="20"/>
</RollingFile>
<!-- 异步日志 -->
<Async name="Async">
<AppenderRef ref="RollingFile"/>
</Async>
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="Console"/>
<AppenderRef ref="Async"/>
</Root>
<Logger name="com.example.logging" level="debug" additivity="false">
<AppenderRef ref="Console"/>
<AppenderRef ref="RollingFile"/>
</Logger>
</Loggers>
</Configuration>
Java日志实现
1 主应用类
package com.example.logging;
import lombok.extern.slf4j.Slf4j;
import com.example.logging.service.UserService;
@Slf4j // Lombok自动生成log对象
public class MainApp {
// 不使用Lombok的传统写法
// private static final Logger log = LoggerFactory.getLogger(MainApp.class);
public static void main(String[] args) {
log.info("应用程序启动...");
// 测试不同日志级别
log.debug("调试信息:参数初始化完成");
log.info("业务日志:用户登录成功");
log.warn("警告信息:磁盘空间不足");
log.error("错误信息:数据库连接失败", new RuntimeException("连接超时"));
// 参数化日志(避免字符串拼接)
String username = "张三";
int age = 25;
log.info("用户信息:姓名={}, 年龄={}", username, age);
// 创建服务对象并调用
UserService userService = new UserService();
userService.createUser("张三");
log.info("应用程序正常关闭");
}
}
2 业务服务类
package com.example.logging.service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class UserService {
private static final Logger log = LoggerFactory.getLogger(UserService.class);
public void createUser(String username) {
log.debug("开始创建用户,用户名:{}", username);
try {
// 模拟业务逻辑
validateUsername(username);
saveToDatabase(username);
sendNotification(username);
log.info("用户创建成功:{}", username);
} catch (Exception e) {
log.error("创建用户失败:{}", username, e);
throw new RuntimeException("创建用户失败", e);
}
}
private void validateUsername(String username) {
if (username == null || username.trim().isEmpty()) {
log.warn("用户名为空,验证失败");
throw new IllegalArgumentException("用户名不能为空");
}
log.debug("用户名验证通过");
}
private void saveToDatabase(String username) {
// 模拟数据库操作
log.debug("保存用户信息到数据库:{}", username);
// 模拟耗时操作
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
log.debug("数据库保存完成");
}
private void sendNotification(String username) {
log.debug("发送用户创建通知,用户:{}", username);
// 模拟发送通知
log.debug("通知发送完成");
}
}
测试类
package com.example.logging;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.LoggerContext;
public class LogTest {
private static final Logger log = LoggerFactory.getLogger(LogTest.class);
@Test
public void testLogLevels() {
log.trace("这是TRACE级别日志");
log.debug("这是DEBUG级别日志");
log.info("这是INFO级别日志");
log.warn("这是WARN级别日志");
log.error("这是ERROR级别日志");
}
@Test
public void testDynamicLogLevel() {
// 动态修改日志级别
ch.qos.logback.classic.Logger rootLogger =
(ch.qos.logback.classic.Logger) LoggerFactory.getLogger(
ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME
);
rootLogger.setLevel(Level.DEBUG);
log.debug("日志级别已更改为DEBUG");
rootLogger.setLevel(Level.ERROR);
log.debug("这条日志不会输出"); // 不会输出
log.error("只能输出ERROR级别日志");
}
@Test
public void testMdcLogging() {
// MDC (Mapped Diagnostic Context) 使用
org.slf4j.MDC.put("userId", "1001");
org.slf4j.MDC.put("requestId", "REQ-001");
log.info("用户请求处理中...");
// 使用时需要添加MDC转换器到日志配置
// <pattern>%d{...} [%X{userId}] [%X{requestId}] %msg%n</pattern>
org.slf4j.MDC.clear();
}
}
日志最佳实践示例
package com.example.logging;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LoggingBestPractices {
private static final Logger log = LoggerFactory.getLogger(LoggingBestPractices.class);
// 1. 使用参数化日志(避免字符串拼接)
public void parameterizedLogging(String username, int age) {
// 推荐:使用占位符
log.info("用户登录:{},年龄:{}", username, age);
// 不推荐:字符串拼接(即使日志级别不输出,也会执行拼接)
// log.info("用户登录:" + username + ",年龄:" + age);
}
// 2. 条件日志(避免不必要的对象创建)
public void conditionalLogging(ComplexObject obj) {
// 推荐:使用占位符,SLF4J内部会判断
log.debug("复杂对象处理:{}", obj);
// 或者使用Lambda表达式(SLF4J 2.0+)
log.atDebug()
.setMessage("复杂对象处理:{}")
.addArgument(obj)
.log();
}
// 3. 异常日志完整记录
public void exceptionLogging() {
try {
// 业务代码
throw new RuntimeException("业务异常");
} catch (Exception e) {
// 推荐:记录完整的异常堆栈
log.error("业务处理异常:{}", e.getMessage(), e);
// 不推荐:只记录异常消息,丢失堆栈信息
// log.error("业务处理异常:" + e.getMessage());
}
}
// 4. 日志级别使用指南
public void logLevelGuide() {
// TRACE: 详细的执行流程,仅在开发调试时使用
log.trace("进入方法,参数:{}", getParameters());
// DEBUG: 调试信息,分析问题使用
log.debug("数据库查询结果:{},影响行数:{}", getResult(), getRowCount());
// INFO: 关键业务节点
log.info("用户[{}]下单成功,订单号:{}", getUserId(), getOrderId());
// WARN: 潜在问题
log.warn("磁盘使用率达到{}%,请关注", getDiskUsage());
// ERROR: 错误事件,需要人工处理
log.error("支付服务调用失败,错误码:{}", getErrorCode());
}
// 辅助方法
private String getParameters() { return "test"; }
private String getResult() { return "success"; }
private int getRowCount() { return 10; }
private String getUserId() { return "1001"; }
private String getOrderId() { return "ORDER-001"; }
private double getDiskUsage() { return 85.5; }
private String getErrorCode() { return "E1001"; }
static class ComplexObject {
@Override
public String toString() {
return "ComplexObject{}";
}
}
}
运行说明
- 选择日志框架:移除不需要的依赖,保留一个实现即可
- 配置文件位置:放在
resources目录下 - 运行主类:执行
MainApp.main()方法 - 查看日志:
- 控制台实时输出
logs/目录下生成日志文件- 错误日志单独记录在
error.log
常见问题
- 日志不输出:检查配置文件位置和日志级别设置
- 性能问题:使用异步日志(AsyncAppender)
- 日志混乱:统一使用SLF4J门面,避免混用不同框架
- 重复日志:检查
additivity属性配置
这个案例涵盖了Java日志的主流实践,您可以根据实际需求选择使用。
版权声明:足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播