本文目录导读:

我来详细说明Java日志记录的规范化流程,涵盖从框架选择到日志管理的最佳实践。
日志框架选型
当前主流方案
<!-- SLF4J + Logback(推荐) -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.9</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.4.11</version>
</dependency>
日志门面与实现的选择
// 推荐使用SLF4J门面,避免直接依赖具体实现
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class UserService {
private static final Logger log = LoggerFactory.getLogger(UserService.class);
}
日志级别规范
级别使用指南
// ERROR:系统异常、业务失败
log.error("用户登录失败,userId: {}, error: {}", userId, error);
// WARN:非预期但可处理的情况
log.warn("数据库连接池达到警告阈值,当前使用率: {}%", usageRate);
// INFO:重要的业务变更、系统状态
log.info("用户订单创建成功,orderId: {}, amount: {}", orderId, amount);
// DEBUG:调试信息,生产环境应关闭
log.debug("SQL执行耗时: {}ms, SQL: {}", time, sql);
// TRACE:更详细的跟踪信息
log.trace("进入方法: {}, 参数: {}", methodName, params);
级别最佳实践
# logback-spring.xml 环境配置
# 开发环境
<springProfile name="dev">
<root level="DEBUG"/>
</springProfile>
# 测试环境
<springProfile name="test">
<root level="INFO"/>
</springProfile>
# 生产环境
<springProfile name="prod">
<root level="WARN"/>
</springProfile>
规范
必须包含的信息
// 标准日志模板
log.info("[业务模块] 操作描述 - 关键参数: {}, 请求追踪ID: {}",
params, traceId);
信息层级结构
// 1. 基础信息
Logger log = LoggerFactory.getLogger(ClassName.class);
// 2. 操作描述
log.info("【订单模块】创建订单");
// 3. 上下文信息
MDC.put("traceId", UUID.randomUUID().toString());
MDC.put("userId", currentUser.getId());
// 4. 业务数据
log.info("订单信息: order={}", JSON.toJSONString(order));
敏感信息处理
// 密码脱敏
log.info("用户登录信息: username={}, password={***}, ip={}",
username, ip);
// 手机号脱敏
String maskedPhone = phone.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
log.info("用户手机号: {}", maskedPhone);
日志配置规范
Logback配置文件示例
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 定义变量 -->
<property name="LOG_PATH" value="/var/log/app"/>
<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>
</encoder>
</appender>
<!-- 文件滚动输出 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/app.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/app.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${LOG_PATTERN}</pattern>
</encoder>
</appender>
</configuration>
日志性能优化
避免字符串拼接
// 不推荐
log.debug("查询用户信息, userId=" + userId + ", count=" + list.size());
// 推荐(使用占位符)
log.debug("查询用户信息, userId: {}, count: {}", userId, list.size());
条件日志
// 不推荐(即使级别不匹配也会计算isVisible的值)
log.trace("用户可见性检查: {}", checkUserVisibility());
// 推荐
if (log.isTraceEnabled()) {
log.trace("用户可见性检查: {}", checkUserVisibility());
}
AOP统一日志记录
方法级日志切面
@Aspect
@Component
public class LogAspect {
@Around("@annotation(log)") // 或自定义注解
public Object aroundMethod(ProceedingJoinPoint joinPoint, Loggable log) {
String methodName = joinPoint.getSignature().toShortString();
Object[] args = joinPoint.getArgs();
log.info("【{}】开始执行, 参数: {}", methodName, args);
long startTime = System.currentTimeMillis();
try {
Object result = joinPoint.proceed();
long duration = System.currentTimeMillis() - startTime;
log.info("【{}】执行成功, 耗时: {}ms", methodName, duration);
return result;
} catch (Exception e) {
log.error("【{}】执行异常: {}", methodName, e.getMessage(), e);
throw e;
}
}
}
自定义日志注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Loggable {
String module() default "";
String description() default "";
boolean recordParams() default true;
boolean recordResult() default false;
}
日志管理规范
文件管理策略
<!-- 按天滚动,保留30天 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/app.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxHistory>30</maxHistory>
<timeBasedFileNamingAndTriggeringPolicy>
<!-- 单个文件最大100MB -->
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
日志归档与清理
#!/bin/bash
# 日志清理脚本
LOG_DIR="/var/log/app"
find $LOG_DIR -name "*.log" -mtime +90 -exec rm -f {} \;
最佳实践总结
统一日志格式
// 推荐:JSON格式日志
{
"timestamp": "2024-01-01 10:00:00.000",
"level": "INFO",
"thread": "http-nio-8080-exec-1",
"logger": "com.example.OrderService",
"traceId": "uuid-xxxx",
"module": "订单模块",
"message": "创建订单成功",
"data": {
"orderId": "123456",
"amount": 99.99
},
"duration": "150ms",
"exception": null
}
规范检查清单
- ✅ 使用SLF4J门面
- ✅ 正确使用日志级别
- ✅ 避免参数拼接
- ✅ 关键业务必须记录
- ✅ 敏感信息脱敏
- ✅ 统一日志格式
- ✅ 日志文件轮转
- ✅ 定期清理归档
常见问题规避
// ❌ 不要记录异常后throw
try {
// business logic
} catch (Exception e) {
log.error("操作失败", e);
throw e; // 或在调用方记录
}
// ✅ 只在最终处理异常的地方记录
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public ResponseEntity handleException(Exception e) {
log.error("全局异常处理: {}", e.getMessage(), e);
return ResponseEntity.status(500).build();
}
}
规范的日志记录不仅可以快速定位问题,还能为系统监控、数据分析提供可靠依据,建议根据团队实际情况,将以上规范纳入代码审查流程。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播