Java日志记录流程如何规范

wen java案例 23

本文目录导读:

Java日志记录流程如何规范

  1. 日志框架选型
  2. 日志级别规范
  3. 日志内容规范
  4. 日志配置规范
  5. 日志性能优化
  6. AOP统一日志记录
  7. 日志管理规范
  8. 最佳实践总结

我来详细说明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();
    }
}

规范的日志记录不仅可以快速定位问题,还能为系统监控、数据分析提供可靠依据,建议根据团队实际情况,将以上规范纳入代码审查流程。

抱歉,评论功能暂时关闭!