Log4j2 使用规则文档

一、框架概述

1.1 什么是 Log4j2

Apache Log4j2 是 Log4j 的升级版本,是一个功能强大的日志框架,提供了:

  • 高性能: 异步日志记录,性能比 Log4j 1.x 和 Logback 更好
  • 灵活配置: 支持 XML、JSON、YAML、Properties 等多种配置格式
  • 插件化架构: 支持自定义 Appender、Filter、Layout 等组件
  • 自动重载配置: 支持配置文件热更新(无需重启应用)
  • 多种输出方式: 支持控制台、文件、数据库、JMS、Socket 等多种输出目标

1.2 技术栈

  • Log4j2 版本: 2.20.0
  • Spring Boot: 2.7.18
  • Spring Boot Log4j2 Starter: 自动配置 Log4j2
  • Lombok: 可选使用 @Slf4j 注解(但项目中使用原生 Log4j2 API)

1.3 为什么选择 Log4j2

  1. 性能优势: 异步日志性能比 Logback 提升 10-20 倍
  2. 功能强大: 支持更丰富的过滤器和布局器
  3. 配置灵活: 支持多种配置格式和动态配置
  4. 社区活跃: Apache 基金会维护,更新及时
  5. 安全性: 修复了 Log4j 1.x 的安全漏洞

二、依赖配置

2.1 Maven 依赖

pom.xml 中配置:

<properties>
    <log4j2.version>2.20.0</log4j2.version>
</properties>

<dependencies>
    <!-- Spring Boot Starter Web (排除 logback,使用 log4j2) -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <version>${spring-boot.version}</version>
        <exclusions>
            <!-- 排除 Spring Boot 默认的 logback -->
            <exclusion>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-logging</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    
    <!-- Spring Boot Log4j2 Starter -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-log4j2</artifactId>
        <version>${spring-boot.version}</version>
    </dependency>
    
    <!-- Spring Boot Starter Test (也需要排除 logback) -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <version>${spring-boot.version}</version>
        <scope>test</scope>
        <exclusions>
            <exclusion>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-logging</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    
    <!-- 如果使用第三方库(如 MyBatis 中间件),也需要排除其 logback 依赖 -->
    <dependency>
        <groupId>ins.framework</groupId>
        <artifactId>ins-framework-mybatis</artifactId>
        <version>6.2.1-SNAPSHOT</version>
        <exclusions>
            <exclusion>
                <groupId>ch.qos.logback</groupId>
                <artifactId>logback-classic</artifactId>
            </exclusion>
            <exclusion>
                <groupId>ch.qos.logback</groupId>
                <artifactId>logback-core</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
</dependencies>

2.2 依赖说明

关键点:

  • ✅ 必须排除 spring-boot-starter-logging(包含 logback)
  • ✅ 必须添加 spring-boot-starter-log4j2
  • ✅ 第三方依赖中的 logback 也需要排除
  • ✅ Spring Boot 会自动管理 Log4j2 的版本

三、配置文件详解

3.1 配置文件位置

配置文件: src/main/resources/log4j2.xml

Spring Boot 会自动识别以下位置的配置文件(按优先级):

  1. log4j2-spring.xml(推荐,支持 Spring Profile)
  2. log4j2.xml
  3. log4j2.properties
  4. log4j2.json
  5. log4j2.yaml

3.2 配置文件结构

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN" monitorInterval="30">
    <!-- 1. Properties: 定义变量 -->
    <Properties>
        <Property name="LOG_HOME">logs</Property>
        <Property name="LOG_FILE_NAME">monitor</Property>
        <Property name="LOG_PATTERN">%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n</Property>
    </Properties>

    <!-- 2. Appenders: 定义日志输出目标 -->
    <Appenders>
        <!-- 控制台输出 -->
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="${LOG_PATTERN}"/>
            <ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/>
        </Console>
        
        <!-- 文件输出 -->
        <RollingFile name="FileAppender" fileName="${LOG_HOME}/${LOG_FILE_NAME}.log"
                     filePattern="${LOG_HOME}/${LOG_FILE_NAME}-%d{yyyy-MM-dd}-%i.log">
            <PatternLayout pattern="${LOG_PATTERN}"/>
            <Policies>
                <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
                <SizeBasedTriggeringPolicy size="10MB"/>
            </Policies>
            <DefaultRolloverStrategy max="30"/>
        </RollingFile>
    </Appenders>

    <!-- 3. Loggers: 定义日志记录器 -->
    <Loggers>
        <Logger name="pdfc.monitor" level="DEBUG" additivity="false">
            <AppenderRef ref="Console"/>
            <AppenderRef ref="FileAppender"/>
        </Logger>
        
        <Root level="INFO">
            <AppenderRef ref="Console"/>
            <AppenderRef ref="FileAppender"/>
        </Root>
    </Loggers>
</Configuration>

3.3 配置属性说明

3.3.1 Configuration 属性
属性 说明 示例
status Log4j2 内部日志级别 WARNDEBUGERROR
monitorInterval 配置文件监控间隔(秒),0 表示不监控 30(30秒检查一次)

示例:

<Configuration status="WARN" monitorInterval="30">
  • status="WARN": 只输出 WARN 及以上级别的 Log4j2 内部日志
  • monitorInterval="30": 每 30 秒检查一次配置文件是否有更新,支持热更新
3.3.2 Properties 属性定义
<Properties>
    <!-- 日志文件存储目录 -->
    <Property name="LOG_HOME">logs</Property>
    
    <!-- 日志文件名称前缀 -->
    <Property name="LOG_FILE_NAME">monitor</Property>
    
    <!-- 日志输出格式 -->
    <Property name="LOG_PATTERN">%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n</Property>
</Properties>

格式说明:

  • %d{yyyy-MM-dd HH:mm:ss.SSS}: 日期时间(精确到毫秒)
  • [%t]: 线程名
  • %-5level: 日志级别(左对齐,5个字符宽度)
  • %logger{36}: 类名(最多36个字符,超出部分用…表示)
  • %msg: 日志消息
  • %n: 换行符

3.4 Appenders(输出器)详解

3.4.1 Console Appender(控制台输出)
<Console name="Console" target="SYSTEM_OUT">
    <PatternLayout pattern="${LOG_PATTERN}"/>
    <ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/>
</Console>

属性说明:

  • name: Appender 名称(用于 Logger 引用)
  • target: 输出目标,SYSTEM_OUT(标准输出)或 SYSTEM_ERR(标准错误)

过滤器说明:

  • ThresholdFilter: 阈值过滤器
    • level="INFO": 只接受 INFO 及以上级别
    • onMatch="ACCEPT": 匹配时接受
    • onMismatch="DENY": 不匹配时拒绝
3.4.2 RollingFile Appender(滚动文件输出)
<RollingFile name="FileAppender" 
             fileName="${LOG_HOME}/${LOG_FILE_NAME}.log"
             filePattern="${LOG_HOME}/${LOG_FILE_NAME}-%d{yyyy-MM-dd}-%i.log">
    <PatternLayout pattern="${LOG_PATTERN}"/>
    <Policies>
        <!-- 基于时间的滚动策略:每天滚动 -->
        <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
        <!-- 基于文件大小的滚动策略:超过 10MB 滚动 -->
        <SizeBasedTriggeringPolicy size="10MB"/>
    </Policies>
    <!-- 保留最近 30 天的日志文件 -->
    <DefaultRolloverStrategy max="30"/>
</RollingFile>

属性说明:

  • fileName: 当前日志文件路径
  • filePattern: 滚动后的日志文件命名模式
    • %d{yyyy-MM-dd}: 日期格式
    • %i: 序号(同一天多个文件时使用)

滚动策略:

  • TimeBasedTriggeringPolicy: 基于时间的滚动
    • interval="1": 每 1 个时间单位滚动一次
    • modulate="true": 在时间边界(如 0 点)滚动
  • SizeBasedTriggeringPolicy: 基于文件大小的滚动
    • size="10MB": 文件超过 10MB 时滚动

保留策略:

  • DefaultRolloverStrategy: 默认滚动策略
    • max="30": 保留最近 30 个文件(或 30 天)
3.4.3 错误日志单独输出
<RollingFile name="ErrorFileAppender" 
             fileName="${LOG_HOME}/${LOG_FILE_NAME}-error.log"
             filePattern="${LOG_HOME}/${LOG_FILE_NAME}-error-%d{yyyy-MM-dd}-%i.log">
    <PatternLayout pattern="${LOG_PATTERN}"/>
    <!-- 只输出 ERROR 级别的日志 -->
    <ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
    <Policies>
        <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
        <SizeBasedTriggeringPolicy size="10MB"/>
    </Policies>
    <DefaultRolloverStrategy max="30"/>
</RollingFile>
3.4.4 SQL 日志单独输出
<RollingFile name="SqlFileAppender" 
             fileName="${LOG_HOME}/${LOG_FILE_NAME}-sql.log"
             filePattern="${LOG_HOME}/${LOG_FILE_NAME}-sql-%d{yyyy-MM-dd}-%i.log">
    <PatternLayout pattern="${LOG_PATTERN}"/>
    <Policies>
        <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
        <SizeBasedTriggeringPolicy size="10MB"/>
    </Policies>
    <DefaultRolloverStrategy max="30"/>
</RollingFile>

3.5 Loggers(日志记录器)详解

3.5.1 Logger 配置
<Logger name="pdfc.monitor" level="DEBUG" additivity="false">
    <AppenderRef ref="Console"/>
    <AppenderRef ref="FileAppender"/>
    <AppenderRef ref="ErrorFileAppender"/>
</Logger>

属性说明:

  • name: Logger 名称(通常是包名或类名)
  • level: 日志级别(TRACE < DEBUG < INFO < WARN < ERROR < FATAL)
  • additivity="false": 不继承父 Logger 的 Appender(避免重复输出)

AppenderRef:

  • ref: 引用已定义的 Appender 名称
3.5.2 Root Logger
<Root level="INFO">
    <AppenderRef ref="Console"/>
    <AppenderRef ref="FileAppender"/>
    <AppenderRef ref="ErrorFileAppender"/>
</Root>

说明:

  • Root Logger 是所有 Logger 的父 Logger
  • 没有指定 Logger 的类会使用 Root Logger 的配置
  • Root Logger 不能设置 additivity(默认为 true)
3.5.3 项目中的 Logger 配置
<!-- 项目包日志级别 -->
<Logger name="pdfc.monitor" level="DEBUG" additivity="false">
    <AppenderRef ref="Console"/>
    <AppenderRef ref="FileAppender"/>
    <AppenderRef ref="ErrorFileAppender"/>
</Logger>

<!-- DAO 层 SQL 日志 -->
<Logger name="pdfc.monitor.domain.sys.dao" level="DEBUG" additivity="false">
    <AppenderRef ref="Console"/>
    <AppenderRef ref="FileAppender"/>
    <AppenderRef ref="SqlFileAppender"/>
</Logger>

<!-- Spring 框架日志 -->
<Logger name="org.springframework" level="INFO" additivity="false">
    <AppenderRef ref="Console"/>
    <AppenderRef ref="FileAppender"/>
</Logger>

<!-- 数据库连接池日志 -->
<Logger name="com.zaxxer.hikari" level="INFO" additivity="false">
    <AppenderRef ref="Console"/>
    <AppenderRef ref="FileAppender"/>
</Logger>

<!-- JDBC 日志 -->
<Logger name="org.springframework.jdbc" level="DEBUG" additivity="false">
    <AppenderRef ref="Console"/>
    <AppenderRef ref="FileAppender"/>
    <AppenderRef ref="SqlFileAppender"/>
</Logger>

四、代码中的使用方式

4.1 获取 Logger 实例

方式一:使用 LogManager(推荐)
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class SysUserController {
    // 使用 static final 修饰,避免重复创建
    private static final Logger logger = LogManager.getLogger(SysUserController.class);
    
    public void someMethod() {
        logger.info("这是一条日志");
    }
}
方式二:使用 Lombok @Slf4j(可选)
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class MonitorMybatisConfig {
    public void someMethod() {
        // 使用 log 变量(Lombok 自动生成)
        log.info("这是一条日志");
    }
}

注意:

  • 如果使用 @Slf4j,需要确保项目中有 slf4j-api 依赖
  • Log4j2 已经包含了 log4j-slf4j-impl,可以直接使用 @Slf4j
  • 但项目中使用的是原生 Log4j2 API(LogManager),性能更好

4.2 日志级别使用

4.2.1 日志级别说明
级别 说明 使用场景 示例
TRACE 跟踪 最详细的调试信息,通常只在开发时使用 方法进入/退出、变量值
DEBUG 调试 详细的调试信息,用于开发调试 SQL 语句、参数值、执行流程
INFO 信息 重要的业务流程信息 接口调用、操作成功、关键节点
WARN 警告 警告信息,不影响系统运行 参数校验失败、业务警告
ERROR 错误 错误信息,需要关注 异常、系统错误
FATAL 致命 致命错误,可能导致系统崩溃 系统无法恢复的错误
4.2.2 日志级别使用示例
public class SysUserController {
    private static final Logger logger = LogManager.getLogger(SysUserController.class);
    
    @PostMapping("/add")
    public ApiResponse<Integer> add(@RequestBody SysUserVo userVo) {
        // INFO: 记录重要业务流程(接口调用)
        logger.info("收到新增用户请求,用户信息: {}", userVo);
        
        try {
            int result = sysUserService.insert(userVo);
            if (result > 0) {
                // INFO: 操作成功
                logger.info("新增用户成功,用户ID: {}", userVo.getId());
                return new ApiResponse<Integer>(result, "新增成功", result);
            } else {
                // WARN: 业务警告(操作失败但不影响系统)
                logger.warn("新增用户失败,返回结果: {}", result);
                return new ApiResponse<Integer>(-1, "新增失败", result);
            }
        } catch (Exception e) {
            // ERROR: 异常信息
            logger.error("新增用户异常,用户信息: {}", userVo, e);
            return new ApiResponse<Integer>(-1, "新增失败: " + e.getMessage(), 0);
        }
    }
}
public class SysUserServiceImpl {
    private static final Logger logger = LogManager.getLogger(SysUserServiceImpl.class);
    
    public int insert(SysUserVo entity) {
        // DEBUG: 详细的执行流程
        logger.debug("开始执行新增用户操作,用户信息: {}", entity);
        
        if (entity == null) {
            // WARN: 参数校验失败
            logger.warn("新增用户失败,用户信息为空");
            return 0;
        }
        
        try {
            // DEBUG: 参数转换
            SysUser po = BeanConverter.voToPo(entity);
            logger.debug("转换后的PO对象: {}", po);
            
            int result = sysUserDao.insert(po);
            
            // DEBUG: 执行结果
            logger.debug("新增用户操作完成,影响行数: {}", result);
            return result;
        } catch (Exception e) {
            // ERROR: 异常信息(包含堆栈)
            logger.error("新增用户操作异常,用户信息: {}", entity, e);
            throw e;
        }
    }
}

4.3 参数化日志(推荐)

4.3.1 使用占位符
// ✅ 正确:使用占位符(推荐)
logger.info("收到新增用户请求,用户ID: {},用户名: {}", userId, userName);

// ❌ 错误:字符串拼接(不推荐)
logger.info("收到新增用户请求,用户ID: " + userId + ",用户名: " + userName);

优势:

  • 性能更好:只有在日志级别匹配时才会进行字符串拼接
  • 代码更清晰:避免复杂的字符串拼接
  • 支持对象:自动调用 toString() 方法
4.3.2 异常日志
try {
    // 业务代码
} catch (Exception e) {
    // ✅ 正确:将异常作为最后一个参数
    logger.error("新增用户异常,用户信息: {}", userVo, e);
    
    // ❌ 错误:不要将异常放在消息中
    logger.error("新增用户异常: " + e.getMessage());
}

说明:

  • 异常对象作为最后一个参数时,Log4j2 会自动打印异常堆栈
  • 如果异常在消息字符串中,只会打印异常消息,不会打印堆栈

4.4 各层日志使用规范

4.4.1 Controller 层

日志级别: INFO

记录内容:

  • ✅ 请求接收日志(包含请求参数)
  • ✅ 操作成功日志(包含结果信息)
  • ✅ 操作失败日志(包含失败原因)
  • ✅ 异常日志(包含异常堆栈)

示例:

@RestController
@RequestMapping("/api/sys/user")
public class SysUserController {
    private static final Logger logger = LogManager.getLogger(SysUserController.class);
    
    @PostMapping("/add")
    public ApiResponse<Integer> add(@RequestBody SysUserVo userVo) {
        logger.info("收到新增用户请求,用户信息: {}", userVo);
        try {
            int result = sysUserService.insert(userVo);
            if (result > 0) {
                logger.info("新增用户成功,用户ID: {}", userVo.getId());
                return new ApiResponse<Integer>(result, "新增成功", result);
            } else {
                logger.warn("新增用户失败,返回结果: {}", result);
                return new ApiResponse<Integer>(-1, "新增失败", result);
            }
        } catch (Exception e) {
            logger.error("新增用户异常,用户信息: {}", userVo, e);
            return new ApiResponse<Integer>(-1, "新增失败: " + e.getMessage(), 0);
        }
    }
}
4.4.2 Service 层

日志级别: DEBUG

记录内容:

  • ✅ 方法开始执行日志
  • ✅ 参数转换日志(VO → PO)
  • ✅ 操作完成日志(包含影响行数)
  • ✅ 异常日志(包含异常堆栈)

示例:

@Service
public class SysUserServiceImpl implements SysUserService {
    private static final Logger logger = LogManager.getLogger(SysUserServiceImpl.class);
    
    public int insert(SysUserVo entity) {
        logger.debug("开始执行新增用户操作,用户信息: {}", entity);
        
        if (entity == null) {
            logger.warn("新增用户失败,用户信息为空");
            return 0;
        }
        
        try {
            SysUser po = BeanConverter.voToPo(entity);
            logger.debug("转换后的PO对象: {}", po);
            
            int result = sysUserDao.insert(po);
            logger.debug("新增用户操作完成,影响行数: {}", result);
            return result;
        } catch (Exception e) {
            logger.error("新增用户操作异常,用户信息: {}", entity, e);
            throw e;
        }
    }
}
4.4.3 DAO 层

日志级别: DEBUG(由 MyBatis 自动记录)

记录内容:

  • ✅ SQL 语句日志(MyBatis 自动记录)
  • ✅ SQL 参数日志(MyBatis 自动记录)
  • ✅ 执行结果日志(MyBatis 自动记录)

说明:

  • DAO 层通常不需要手动记录日志
  • MyBatis 会自动记录 SQL 执行日志
  • 如果需要,可以在 Mapper XML 中添加注释

MyBatis SQL 日志配置:

# application.properties
mybatis.configuration.log-impl=org.apache.ibatis.logging.slf4j.Slf4jImpl

五、日志文件管理

5.1 日志文件位置

开发环境

日志文件保存在项目根目录下的 logs/ 目录:

monitor_java/
├── logs/
│   ├── monitor.log              # 所有日志
│   ├── monitor-sql.log          # SQL日志
│   ├── monitor-error.log        # 错误日志
│   ├── monitor-2025-11-13-1.log # 滚动日志(按日期和序号)
│   └── ...
生产环境

建议将日志文件配置到独立的日志目录:

Linux:

<Property name="LOG_HOME">/var/log/monitor</Property>

Windows:

<Property name="LOG_HOME">C:\logs\monitor</Property>

5.2 日志文件滚动策略

5.2.1 滚动触发条件
  1. 时间滚动: 每天 0 点自动滚动
  2. 大小滚动: 文件超过 10MB 时滚动
  3. 同时满足: 满足任一条件即滚动
5.2.2 滚动文件命名

当前文件: monitor.log

滚动后文件: monitor-2025-11-13-1.log

  • monitor: 文件名称前缀
  • 2025-11-13: 日期(yyyy-MM-dd)
  • 1: 序号(同一天多个文件时递增)

示例:

monitor.log                    # 当前日志文件
monitor-2025-11-13-1.log      # 2025-11-13 的第一个滚动文件
monitor-2025-11-13-2.log      # 2025-11-13 的第二个滚动文件(如果超过大小)
monitor-2025-11-14-1.log      # 2025-11-14 的第一个滚动文件

5.3 日志文件保留策略

保留天数: 30 天

说明:

  • DefaultRolloverStrategy max="30" 表示保留最近 30 个文件
  • 如果按天滚动,则保留 30 天的日志
  • 如果按大小滚动,则保留最近 30 个文件

修改保留天数:

<DefaultRolloverStrategy max="60"/>  <!-- 改为保留 60 天 -->

5.4 查看日志

5.4.1 实时查看日志
# 查看所有日志
tail -f logs/monitor.log

# 查看 SQL 日志
tail -f logs/monitor-sql.log

# 查看错误日志
tail -f logs/monitor-error.log
5.4.2 搜索日志
# 搜索特定用户的日志
grep "日志测试用户" logs/monitor.log

# 搜索错误日志
grep "ERROR" logs/monitor.log

# 搜索特定接口的日志
grep "新增用户" logs/monitor.log

# 搜索特定时间段的日志(需要日志格式包含时间)
grep "2025-11-13 14:" logs/monitor.log
5.4.3 查看日志文件大小
# 查看日志文件大小
ls -lh logs/

# 查看日志文件总大小
du -sh logs/

六、最佳实践

6.1 日志级别选择

6.1.1 开发环境
<!-- 开发环境:使用 DEBUG 级别,便于调试 -->
<Logger name="pdfc.monitor" level="DEBUG" additivity="false">
    ...
</Logger>
6.1.2 生产环境
<!-- 生产环境:使用 INFO 级别,减少日志量 -->
<Logger name="pdfc.monitor" level="INFO" additivity="false">
    ...
</Logger>

建议:

  • 开发环境:使用 DEBUG 级别,便于调试
  • 测试环境:使用 INFO 级别,平衡日志量和调试需求
  • 生产环境:使用 INFO 级别,减少日志量,提高性能

6.2 日志内容规范

6.2.1 日志消息格式
// ✅ 正确:包含上下文信息
logger.info("收到新增用户请求,用户ID: {},用户名: {}", userId, userName);

// ❌ 错误:缺少上下文信息
logger.info("收到请求");
6.2.2 敏感信息处理
// ❌ 错误:不要记录敏感信息
logger.info("用户登录,密码: {}", password);

// ✅ 正确:脱敏处理
logger.info("用户登录,用户名: {}", userName);

敏感信息包括:

  • 密码、密钥、Token
  • 身份证号、银行卡号
  • 手机号(可部分脱敏)
  • 其他个人隐私信息

6.3 性能优化

6.3.1 使用参数化日志
// ✅ 正确:参数化日志(性能好)
logger.debug("查询用户,用户ID: {},条件: {}", userId, condition);

// ❌ 错误:字符串拼接(性能差)
logger.debug("查询用户,用户ID: " + userId + ",条件: " + condition);

原因:

  • 参数化日志只有在日志级别匹配时才会进行字符串拼接
  • 字符串拼接无论日志级别如何都会执行
6.3.2 避免在循环中记录日志
// ❌ 错误:在循环中记录日志
for (User user : userList) {
    logger.debug("处理用户: {}", user);  // 如果日志级别是 DEBUG,会产生大量日志
}

// ✅ 正确:在循环外记录日志
logger.debug("开始处理用户列表,数量: {}", userList.size());
for (User user : userList) {
    // 处理用户
}
logger.debug("用户列表处理完成");
6.3.3 使用异步日志(可选)

如果需要更高的性能,可以配置异步日志:

<!-- 添加异步日志依赖 -->
<dependency>
    <groupId>com.lmax</groupId>
    <artifactId>disruptor</artifactId>
    <version>3.4.4</version>
</dependency>
<!-- 配置异步 Logger -->
<AsyncLogger name="pdfc.monitor" level="DEBUG" includeLocation="true">
    <AppenderRef ref="Console"/>
    <AppenderRef ref="FileAppender"/>
</AsyncLogger>

6.4 日志格式规范

6.4.1 统一日志格式

项目中使用统一的日志格式:

%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n

输出示例:

2025-11-13 14:30:25.123 [http-nio-8080-exec-1] INFO  pdfc.monitor.web.SysUserController - 收到新增用户请求,用户信息: SysUserVo(id=null, name=测试用户, work=工程师, age=25)
6.4.2 自定义日志格式(可选)

如果需要自定义日志格式,可以修改 LOG_PATTERN

<!-- 添加方法名和行号 -->
<Property name="LOG_PATTERN">%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36}.%M(%L) - %msg%n</Property>

格式说明:

  • %M: 方法名
  • %L: 行号
  • %F: 文件名

注意: 添加方法名和行号会影响性能,生产环境不推荐使用。

七、常见问题

7.1 日志不输出

问题:日志文件没有生成

可能原因:

  1. 日志目录不存在或没有写权限
  2. 日志级别配置过高
  3. Logger 名称不匹配

解决方法:

  1. 检查日志目录是否存在,是否有写权限
  2. 检查日志级别配置
  3. 检查 Logger 名称是否匹配(包名或类名)
问题:控制台没有日志输出

可能原因:

  1. Console Appender 的过滤器级别过高
  2. Logger 的 additivity="false" 但没有引用 Console Appender

解决方法:

  1. 检查 ThresholdFilter 的级别
  2. 确保 Logger 引用了 Console Appender

7.2 日志重复输出

问题:同一条日志输出多次

可能原因:

  1. Logger 的 additivity="true"(默认),会继承父 Logger 的 Appender
  2. 同一个 Appender 被多个 Logger 引用

解决方法:

<!-- 设置 additivity="false",不继承父 Logger 的 Appender -->
<Logger name="pdfc.monitor" level="DEBUG" additivity="false">
    <AppenderRef ref="Console"/>
    <AppenderRef ref="FileAppender"/>
</Logger>

7.3 日志文件过大

问题:日志文件占用磁盘空间过大

解决方法:

  1. 调整日志级别(生产环境使用 INFO)
  2. 减小日志文件大小限制
  3. 减少日志保留天数
  4. 定期清理旧日志文件
<!-- 减小文件大小限制 -->
<SizeBasedTriggeringPolicy size="5MB"/>

<!-- 减少保留天数 -->
<DefaultRolloverStrategy max="7"/>

7.4 配置文件不生效

问题:修改配置文件后不生效

解决方法:

  1. 检查配置文件名称和位置是否正确
  2. 检查配置文件语法是否正确(XML 格式)
  3. 重启应用(如果 monitorInterval="0"
  4. 检查 monitorInterval 配置(支持热更新)
<!-- 启用配置文件监控(30秒检查一次) -->
<Configuration status="WARN" monitorInterval="30">

7.5 Logback 冲突

问题:启动时出现 Logback 相关错误

可能原因:

  1. 没有排除 spring-boot-starter-logging
  2. 第三方依赖引入了 logback

解决方法:

  1. 确保排除了 spring-boot-starter-logging
  2. 排除第三方依赖中的 logback:
<dependency>
    <groupId>ins.framework</groupId>
    <artifactId>ins-framework-mybatis</artifactId>
    <exclusions>
        <exclusion>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
        </exclusion>
        <exclusion>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
        </exclusion>
    </exclusions>
</dependency>

八、配置调整示例

8.1 修改日志级别

<!-- 将 Service 层日志级别改为 INFO -->
<Logger name="pdfc.monitor.service.impl" level="INFO" additivity="false">
    <AppenderRef ref="Console"/>
    <AppenderRef ref="FileAppender"/>
    <AppenderRef ref="ErrorFileAppender"/>
</Logger>

8.2 修改日志文件大小

<!-- 将文件大小限制改为 50MB -->
<SizeBasedTriggeringPolicy size="50MB"/>

8.3 修改日志保留天数

<!-- 将保留天数改为 60 天 -->
<DefaultRolloverStrategy max="60"/>

8.4 添加新的日志文件

<!-- 添加业务日志文件 -->
<RollingFile name="BusinessFileAppender" 
             fileName="${LOG_HOME}/${LOG_FILE_NAME}-business.log"
             filePattern="${LOG_HOME}/${LOG_FILE_NAME}-business-%d{yyyy-MM-dd}-%i.log">
    <PatternLayout pattern="${LOG_PATTERN}"/>
    <Policies>
        <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
        <SizeBasedTriggeringPolicy size="10MB"/>
    </Policies>
    <DefaultRolloverStrategy max="30"/>
</RollingFile>

<!-- 在 Logger 中引用 -->
<Logger name="pdfc.monitor.service" level="INFO" additivity="false">
    <AppenderRef ref="Console"/>
    <AppenderRef ref="FileAppender"/>
    <AppenderRef ref="BusinessFileAppender"/>
</Logger>

九、总结

9.1 核心要点

  1. 依赖配置: 必须排除 logback,添加 log4j2
  2. 配置文件: log4j2.xml 放在 src/main/resources/ 目录
  3. 日志级别: 开发环境 DEBUG,生产环境 INFO
  4. 参数化日志: 使用占位符 {},不要字符串拼接
  5. 异常日志: 异常对象作为最后一个参数
  6. 日志文件: 配置滚动策略和保留策略

9.2 项目配置总结

已完成的配置:

  • ✅ 依赖已添加(排除 logback,使用 log4j2)
  • ✅ 配置文件已创建(log4j2.xml)
  • ✅ Controller 层日志已添加(INFO 级别)
  • ✅ Service 层日志已添加(DEBUG 级别)
  • ✅ DAO 层日志已配置(MyBatis 自动记录)
  • ✅ 日志文件正常生成(monitor.log、monitor-sql.log、monitor-error.log)
  • ✅ 日志滚动策略已配置(按时间和大小滚动)
  • ✅ 日志保留策略已配置(保留 30 天)

9.3 使用建议

  1. 开发阶段: 使用 DEBUG 级别,便于调试
  2. 生产阶段: 使用 INFO 级别,减少日志量
  3. 异常处理: 始终记录异常堆栈
  4. 敏感信息: 不要记录密码、密钥等敏感信息
  5. 性能考虑: 使用参数化日志,避免在循环中记录日志
  6. 日志管理: 定期清理旧日志文件,避免磁盘空间不足

文档版本: 1.0
最后更新: 2025-11-13
维护者: 项目团队

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐