本文目录导读:

我将为您提供一个全面的Java日志框架案例,涵盖主流日志框架的使用和最佳实践。
常用日志框架对比
<!-- pom.xml 依赖配置 -->
<dependencies>
<!-- SLF4J API - 日志门面 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.36</version>
</dependency>
<!-- Logback - 日志实现 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.11</version>
</dependency>
<!-- Log4j2 - 替代Log4j 1.x -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.20.0</version>
</dependency>
<!-- 兼容旧版log4j -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<version>1.7.36</version>
</dependency>
<!-- Lombok的@Slf4j注解(可选) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>
</dependencies>
基础使用案例
1 标准SLF4J使用
package com.example.logging;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 日志使用基础案例
*/
public class BasicLoggingExample {
// 推荐使用private static final
private static final Logger logger = LoggerFactory.getLogger(BasicLoggingExample.class);
public void demonstrateLogLevels() {
// 不同级别的日志
logger.trace("这是TRACE级别日志");
logger.debug("这是DEBUG级别日志,用户ID: {}", 1001);
logger.info("这是INFO级别日志");
logger.warn("这是WARN级别日志");
logger.error("这是ERROR级别日志", new RuntimeException("示例异常"));
}
public void demonstrateParameterizedLogging() {
String username = "张三";
int age = 25;
// 使用占位符,避免字符串拼接
logger.debug("用户{},年龄{},正在登录系统", username, age);
// 只有debug级别启用时才执行方法
if (logger.isDebugEnabled()) {
logger.debug("复杂的调试信息: " + expensiveMethod());
}
}
private String expensiveMethod() {
// 模拟昂贵操作
return "计算结果";
}
// 使用Lombok简化
// @Slf4j
public static void main(String[] args) {
BasicLoggingExample example = new BasicLoggingExample();
example.demonstrateLogLevels();
example.demonstrateParameterizedLogging();
}
}
2 MDC(Mapped Diagnostic Context)使用
package com.example.logging;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import java.util.UUID;
/**
* MDC用于跟踪请求上下文
*/
public class MDCExample {
private static final Logger logger = LoggerFactory.getLogger(MDCExample.class);
public void processRequest(String userId) {
// 在请求开始时设置MDC
MDC.put("requestId", UUID.randomUUID().toString());
MDC.put("userId", userId);
try {
logger.info("开始处理用户请求");
// 业务逻辑
doBusinessLogic();
logger.info("请求处理完成");
} catch (Exception e) {
logger.error("处理请求失败", e);
} finally {
// 确保清理MDC,避免内存泄漏
MDC.clear();
}
}
private void doBusinessLogic() {
logger.debug("执行业务逻辑,当前用户: {}", MDC.get("userId"));
}
}
Logback配置案例
1 logback.xml 完整配置
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 定义属性 -->
<property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"/>
<property name="LOG_PATH" value="./logs"/>
<!-- 控制台输出 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} %highlight(%-5level) %cyan(%logger{36}) - %msg%n</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}/archive/application-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxHistory>30</maxHistory>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>50MB</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}/archive/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_FILE" class="ch.qos.logback.classic.AsyncAppender">
<queueSize>512</queueSize>
<discardingThreshold>0</discardingThreshold>
<appender-ref ref="FILE"/>
</appender>
<!-- 不同包的日志级别设置 -->
<logger name="org.springframework" level="WARN"/>
<logger name="com.example" level="DEBUG"/>
<!-- 根日志级别 -->
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="ASYNC_FILE"/>
<appender-ref ref="ERROR_FILE"/>
</root>
</configuration>
2 动态配置示例
package com.example.logging;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.LoggerContext;
import org.slf4j.LoggerFactory;
public class DynamicLogLevelExample {
public void changeLogLevel(String packageName, String level) {
Object obj = LoggerFactory.getILoggerFactory();
if (obj instanceof LoggerContext) {
LoggerContext context = (LoggerContext) obj;
ch.qos.logback.classic.Logger logger = context.getLogger(packageName);
// 动态修改日志级别
logger.setLevel(Level.toLevel(level));
}
}
}
Log4j2配置案例
1 log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Properties>
<Property name="LOG_PATTERN">%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n</Property>
<Property name="LOG_PATH">./logs</Property>
</Properties>
<Appenders>
<!-- 控制台 -->
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} %highlight{%-5level} %style{%logger{36}}{cyan} - %msg%n"/>
</Console>
<!-- 滚动文件 -->
<RollingFile name="RollingFile" fileName="${LOG_PATH}/app.log"
filePattern="${LOG_PATH}/app-%d{yyyy-MM-dd}-%i.log">
<PatternLayout pattern="${LOG_PATTERN}"/>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="50MB"/>
</Policies>
<DefaultRolloverStrategy max="20"/>
</RollingFile>
<!-- 异步文件 -->
<Async name="AsyncFile">
<AppenderRef ref="RollingFile"/>
<BufferSize>1024</BufferSize>
</Async>
</Appenders>
<Loggers>
<Logger name="org.springframework" level="WARN"/>
<Logger name="com.example" level="DEBUG"/>
<Root level="INFO">
<AppenderRef ref="Console"/>
<AppenderRef ref="AsyncFile"/>
</Root>
</Loggers>
</Configuration>
高级应用案例
1 日志分类使用
package com.example.logging;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 业务日志案例
*/
public class BusinessLogExample {
private static final Logger BUSINESS_LOGGER = LoggerFactory.getLogger("business");
private static final Logger SECURITY_LOGGER = LoggerFactory.getLogger("security");
private static final Logger OPERATION_LOGGER = LoggerFactory.getLogger("operation");
public void businessOperation(String orderId, String userId) {
// 业务日志
BUSINESS_LOGGER.info("用户{}创建订单:{}", userId, orderId);
// 安全审计日志
SECURITY_LOGGER.info("用户操作:{} 执行创建订单操作,订单号:{}", userId, orderId);
// 操作日志
OPERATION_LOGGER.info("订单创建成功 | 操作人:{} | 订单号:{} | 时间:{}",
userId, orderId, System.currentTimeMillis());
}
public void throwException() {
try {
int result = 10 / 0;
} catch (Exception e) {
// 记录异常时的标准做法
logger.error("计算异常,参数: a={}, b={}", 10, 0, e);
}
}
private static final Logger logger = LoggerFactory.getLogger(BusinessLogExample.class);
}
2 日志埋点与性能监控
package com.example.logging;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class PerformanceLogExample {
private static final Logger logger = LoggerFactory.getLogger(PerformanceLogExample.class);
public void logWithTime() {
long start = System.currentTimeMillis();
try {
// 模拟业务逻辑
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
long cost = System.currentTimeMillis() - start;
// 性能日志,超过阈值告警
if (cost > 50) {
logger.warn("方法执行耗时过长: {} ms", cost);
} else {
logger.debug("方法执行耗时: {} ms", cost);
}
}
}
// 使用AOP实现方法性能监控
// @Around("execution(* com.example..*.*(..))")
// public Object logPerformance(ProceedingJoinPoint pjp) throws Throwable {
// long start = System.currentTimeMillis();
// Object result = pjp.proceed();
// long cost = System.currentTimeMillis() - start;
// logger.info("方法 {} 执行耗时: {}ms", pjp.getSignature(), cost);
// return result;
// }
}
3 Spring Boot配置
# application.yml
logging:
level:
root: INFO
com.example: DEBUG
org.springframework: WARN
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"
file: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"
file:
name: ./logs/spring-boot-application.log
max-size: 10MB
max-history: 30
4 审计日志
package com.example.logging;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Component
public class AuditLogService {
private static final Logger auditLogger = LoggerFactory.getLogger("AUDIT");
private final ObjectMapper objectMapper = new ObjectMapper();
public void logAction(String user, String action, Object data) {
AuditLog auditLog = new AuditLog();
auditLog.setUserId(user);
auditLog.setAction(action);
auditLog.setTimestamp(System.currentTimeMillis());
try {
String json = objectMapper.writeValueAsString(data);
auditLog.setData(json);
} catch (Exception e) {
auditLog.setData("序列化失败");
}
// 写入审计日志
auditLogger.info("AUDIT - {}", toJsonString(auditLog));
}
private String toJsonString(Object obj) {
try {
return objectMapper.writeValueAsString(obj);
} catch (Exception e) {
return obj.toString();
}
}
static class AuditLog {
private String userId;
private String action;
private long timestamp;
private String data;
// getters and setters...
public String getUserId() { return userId; }
public void setUserId(String userId) { this.userId = userId; }
public String getAction() { return action; }
public void setAction(String action) { this.action = action; }
public long getTimestamp() { return timestamp; }
public void setTimestamp(long timestamp) { this.timestamp = timestamp; }
public String getData() { return data; }
public void setData(String data) { this.data = data; }
}
}
最佳实践总结
package com.example.logging;
/**
* 日志最佳实践总结
*/
public class LoggingBestPractices {
/**
* 1. 使用SLF4J门面而不是直接使用具体实现
* 2. 使用参数化日志,避免字符串拼接
* 3. 正确处理异常日志
* 4. 合理设置日志级别
* 5. 清理MDC上下文
*/
private static final org.slf4j.Logger logger =
org.slf4j.LoggerFactory.getLogger(LoggingBestPractices.class);
public void rules() {
// 1. 使用占位符而不是字符串拼接
String name = "test";
int id = 100;
logger.debug("用户: {}, ID: {}", name, id); // 正确
logger.debug("用户: " + name + ", ID: " + id); // 错误
// 2. 完整异常信息
try {
// 业务代码
} catch (Exception e) {
logger.error("业务操作失败", e); // 正确 - 打印完整堆栈
// logger.error("业务操作失败: {}", e.getMessage()); // 错误 - 丢失堆栈
}
// 3. 避免记录敏感信息
// 不要: logger.info("用户密码: {}", password);
// 4. 使用debug级别记录调试信息
logger.debug("进入方法,参数: {}", requestParams);
// 5. 日志级别选择
// TRACE: 详细调试信息
// DEBUG: 调试信息
// INFO: 关键业务信息
// WARN: 可恢复的问题
// ERROR: 异常错误
}
}
日志框架性能对比
| 框架 | 特性 | 同步性能 | 异步性能 | 适用场景 |
|---|---|---|---|---|
| Logback | 实现简单,功能完善 | 中小型项目 | ||
| Log4j2 | 强大的过滤和路由 | 大型系统 | ||
| Java Util Logging | JDK自带 | 简单应用 |
常见问题解决
// 1. 避免日志框架冲突
// 排除冲突依赖
<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>
// 2. 循环依赖问题
// 在logback.xml中配置
<configuration>
<statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" />
</configuration>
这个案例涵盖了Java日志框架的主要使用场景和实践技巧,根据项目需求选择合适的技术方案,并遵循最佳实践来保证日志系统的健壮性和可用性。