Spring Boot项目集成日志系统使用完整指南

Spring Boot项目集成日志系统使用指南

目录

Java日志系统简介

1. 日志系统的作用

日志系统是软件开发中不可或缺的组成部分,主要用于:

  • 问题诊断: 记录程序运行状态和错误信息
  • 性能监控: 记录方法执行时间和资源使用情况
  • 审计追踪: 记录用户操作和系统行为
  • 运维支持: 提供系统运行状态和异常信息

2. Java日志框架发展历程

Java 1.4 (2002) → Logging API (内置)
    ↓
Apache Log4j 1.x (2001-2015)
    ↓
SLF4J + Logback (2006-至今) ← 推荐使用
    ↓
Log4j 2.x (2012-至今) ← 高性能选择

3. 主流日志框架对比

框架优势劣势适用场景
Log4j 1.x成熟稳定,配置灵活已停止维护,性能一般老项目维护
Log4j 2.x性能优秀,功能丰富配置相对复杂高性能要求
Logback性能好,配置简单功能相对简单Spring Boot默认
SLF4J抽象层,统一接口需要配合具体实现框架集成

日志类型详解

1. 日志级别 (Log Levels)

1.1 标准日志级别
// 从低到高的日志级别
TRACE < DEBUG < INFO < WARN < ERROR < FATAL

// 实际使用中的日志级别
DEBUG  // 调试信息,开发环境使用
INFO   // 一般信息,记录程序运行状态
WARN   // 警告信息,潜在问题但不影响运行
ERROR  // 错误信息,程序异常但可恢复
1.2 日志级别使用场景
@Log4j2
public class UserService {
  
    public User getUserById(Long id) {
        // DEBUG: 详细的调试信息
        log.debug("开始查询用户,ID: {}", id);
      
        try {
            User user = userMapper.selectById(id);
          
            // INFO: 重要的业务信息
            log.info("成功查询用户: {}", user.getUsername());
          
            return user;
        } catch (Exception e) {
            // ERROR: 异常信息
            log.error("查询用户失败,ID: {}, 错误: {}", id, e.getMessage(), e);
            throw new RuntimeException("用户查询失败", e);
        }
    }
}

2. 日志输出目标 (Appenders)

2.1 控制台输出
<!-- Logback配置 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
        <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
</appender>
2.2 文件输出
<!-- 滚动文件输出 -->
<appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>./logs/application.log</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <fileNamePattern>./logs/application.%d{yyyy-MM-dd}.log</fileNamePattern>
        <maxHistory>30</maxHistory>
    </rollingPolicy>
    <encoder>
        <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
</appender>
2.3 数据库输出
<!-- 数据库输出 -->
<appender name="db" class="ch.qos.logback.classic.db.DBAppender">
    <connectionSource class="ch.qos.logback.core.db.DriverManagerConnectionSource">
        <driverClass>com.mysql.cj.jdbc.Driver</driverClass>
        <url>jdbc:mysql://localhost:3306/logs</url>
        <user>root</user>
        <password>password</password>
    </connectionSource>
</appender>

3. 日志格式 (Patterns)

3.1 常用格式占位符
<!-- 标准日志格式 -->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>

<!-- 格式说明 -->
%d{yyyy-MM-dd HH:mm:ss.SSS}  <!-- 时间戳 -->
[%thread]                     <!-- 线程名 -->
%-5level                      <!-- 日志级别,左对齐5位 -->
%logger{36}                   <!-- 类名,最大36字符 -->
%msg                          <!-- 日志消息 -->
%n                            <!-- 换行符 -->
3.2 自定义格式示例
<!-- 包含MDC信息的格式 -->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{traceId}] [%X{userId}] %-5level %logger{36} - %msg%n</pattern>

<!-- 包含调用位置的格式 -->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36}:%line - %msg%n</pattern>

Spring Boot日志配置

1. Spring Boot日志自动配置

1.1 默认配置

Spring Boot默认使用Logback作为日志实现,提供以下自动配置:

# application.yml
logging:
  level:
    root: INFO
    com.zmy.enrollment: DEBUG
    org.springframework.web: 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/application.log
    max-size: 10MB
    max-history: 30
1.2 依赖配置
<!-- Spring Boot Starter Web 自动包含日志依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- 显式添加日志依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-logging</artifactId>
</dependency>

2. 项目实际配置分析

2.1 主配置文件 (application.yml)
# Logger Config
logging:
  level:
    com:
      baomidou:
        mybatisplus: ERROR      # MyBatis Plus日志级别
      zmy:
        enrollment: DEBUG       # 项目业务日志级别
    org:
      springframework:
        web:
          client: ERROR         # Spring Web客户端日志级别
    springfox:
      documentation: ERROR      # Swagger文档日志级别
2.2 Logback配置文件 (logback-spring.xml)
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
    <contextName>logback</contextName>
  
    <!-- 控制台输出 -->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %contextName [%thread] [%X{traceId}] [%X{userId}-%X{loginName}-%X{username}] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>
  
    <!-- 应用日志文件 -->
    <appender name="file" class="ch.qos.logback.classic.sift.SiftingAppender">
        <discriminator>
            <Key>systemFlag</Key>
            <DefaultValue>zmbm</DefaultValue>
        </discriminator>
        <sift>
            <appender name="EXTRACTOR_LOG_${systemFlag}" class="ch.qos.logback.core.rolling.RollingFileAppender">
                <File>./logs/${systemFlag}/application.log</File>
                <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                    <fileNamePattern>./logs/${systemFlag}/%d{yyyy-MM-dd}/%d{yyyy-MM-dd}-%i.log.gz</fileNamePattern>
                    <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                        <maxFileSize>512MB</maxFileSize>
                    </timeBasedFileNamingAndTriggeringPolicy>
                    <maxHistory>60</maxHistory>
                </rollingPolicy>
                <encoder>
                    <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %contextName [%thread] [%X{traceId}] %-5level %logger{36} - %msg%n</pattern>
                </encoder>
            </appender>
        </sift>
    </appender>
  
    <!-- 根日志配置 -->
    <root level="info">
        <appender-ref ref="console" />
        <appender-ref ref="file" />
    </root>
  
    <!-- 特定包日志配置 -->
    <logger name="org.springframework.scheduling" level="info" />
    <logger name="com.zmy.enrollment.web.base.filter" level="info" />
    <logger name="com.zmy.enrollment.web.zmbm.task" level="info" />
</configuration>

3. 代码中的日志使用

3.1 Lombok注解使用
// 使用@Log4j2注解
@Log4j2
@RestController
@RequestMapping("/api/user")
public class UserController {
  
    @GetMapping("/{id}")
    public ResponseEntity<User> getUser(@PathVariable Long id) {
        log.info("查询用户信息,ID: {}", id);
      
        try {
            User user = userService.getUserById(id);
            log.debug("用户查询成功: {}", user);
            return ResponseEntity.ok(user);
        } catch (Exception e) {
            log.error("查询用户失败,ID: {}, 错误: {}", id, e.getMessage(), e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }
}

// 使用@Slf4j注解
@Slf4j
@Service
public class UserService {
  
    public void processUser(User user) {
        log.info("开始处理用户: {}", user.getUsername());
      
        if (log.isDebugEnabled()) {
            log.debug("用户详细信息: {}", user);
        }
      
        // 业务逻辑处理
        log.info("用户处理完成: {}", user.getUsername());
    }
}
3.2 手动创建Logger
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class CustomService {
    private static final Logger logger = LoggerFactory.getLogger(CustomService.class);
  
    public void doSomething() {
        logger.info("开始执行任务");
      
        try {
            // 业务逻辑
            logger.debug("任务执行中...");
        } catch (Exception e) {
            logger.error("任务执行失败", e);
        }
    }
}

项目实际配置分析

1. 多环境日志配置

1.1 开发环境配置
<!-- logback-spring.xml -->
<springProfile name="dev">
    <root level="debug">
        <appender-ref ref="console" />
        <appender-ref ref="file" />
    </root>
</springProfile>
1.2 生产环境配置
<!-- logback-spring.xml -->
<springProfile name="prod">
    <root level="warn">
        <appender-ref ref="file" />
        <appender-ref ref="traceFile" />
    </root>
</springProfile>

2. 特殊日志配置

2.1 追踪日志 (Trace Log)
<!-- 追踪日志,用于性能监控 -->
<appender name="traceFile" class="ch.qos.logback.classic.sift.SiftingAppender">
    <discriminator>
        <Key>systemFlag</Key>
        <DefaultValue>zmbm</DefaultValue>
    </discriminator>
    <sift>
        <appender name="EXTRACTOR_LOG_${systemFlag}" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <File>/data/logstash/${systemFlag}/trace.log</File>
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <fileNamePattern>/data/logstash/${systemFlag}/%d{yyyy-MM-dd}/trace-%d{yyyy-MM-dd}-%i.log.gz</fileNamePattern>
                <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                    <maxFileSize>512MB</maxFileSize>
                </timeBasedFileNamingAndTriggeringPolicy>
                <maxHistory>60</maxHistory>
            </rollingPolicy>
            <!-- JSON格式输出,便于ELK分析 -->
            <encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
                <providers>
                    <pattern>
                        <pattern>
                        {
                            "timestamp": "%d{yyyy-MM-dd HH:mm:ss.SSS}",
                            "traceId": "%X{traceId}",
                            "userId": "%X{userId}",
                            "level": "%level",
                            "message": "%message"
                        }
                        </pattern>
                    </pattern>
                </providers>
            </encoder>
        </appender>
    </sift>
</appender>
2.2 第三方接口日志
<!-- 第三方接口调用日志 -->
<appender name="thirdFile" class="ch.qos.logback.classic.sift.SiftingAppender">
    <discriminator>
        <Key>systemFlag</Key>
        <DefaultValue>zmbm</DefaultValue>
    </discriminator>
    <sift>
        <appender name="EXTRACTOR_LOG_${systemFlag}" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <File>./logs/${systemFlag}/thrid.log</File>
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <fileNamePattern>./logs/${systemFlag}/%d{yyyy-MM-dd}/thrid-%d{yyyy-MM-dd}-%i.log.gz</fileNamePattern>
                <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                    <maxFileSize>512MB</maxFileSize>
                </timeBasedFileNamingAndTriggeringPolicy>
                <maxHistory>60</maxHistory>
            </rollingPolicy>
            <encoder>
                <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %contextName [%thread] [%X{traceId}] %-5level %logger{36} - %msg%n</pattern>
            </encoder>
        </appender>
    </sift>
</appender>

3. 日志分类管理

3.1 按业务模块分类
<!-- 不同业务模块使用不同的日志文件 -->
<logger name="com.zmy.enrollment.web.zmbm.activity" level="info" additivity="false">
    <appender-ref ref="activityFile" />
</logger>

<logger name="com.zmy.enrollment.web.zmbm.registration" level="info" additivity="false">
    <appender-ref ref="registrationFile" />
</logger>

<logger name="com.zmy.enrollment.web.zmbm.lottery" level="info" additivity="false">
    <appender-ref ref="lotteryFile" />
</logger>
3.2 按日志级别分类
<!-- 错误日志单独输出 -->
<appender name="errorFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>./logs/error.log</file>
    <filter class="ch.qos.logback.classic.filter.LevelFilter">
        <level>ERROR</level>
        <onMatch>ACCEPT</onMatch>
        <onMismatch>DENY</onMismatch>
    </filter>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <fileNamePattern>./logs/error.%d{yyyy-MM-dd}.log</fileNamePattern>
        <maxHistory>90</maxHistory>
    </rollingPolicy>
    <encoder>
        <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
</appender>

重难点分析

1. 日志性能优化

难点描述
  • 日志输出对应用性能的影响
  • 大量日志导致的磁盘I/O问题
  • 日志级别判断的性能开销
解决方案
// 使用条件日志,避免不必要的字符串拼接
@Log4j2
public class PerformanceService {
  
    public void processData(List<Data> dataList) {
        // 避免在非DEBUG级别时执行复杂操作
        if (log.isDebugEnabled()) {
            log.debug("处理数据列表,数量: {}, 详情: {}", dataList.size(), dataList);
        }
      
        // 使用占位符,避免字符串拼接
        log.info("开始处理数据,数量: {}", dataList.size());
      
        // 异步日志处理
        log.info("数据处理完成,耗时: {}ms", System.currentTimeMillis() - startTime);
    }
}
<!-- 异步日志配置 -->
<appender name="asyncFile" class="ch.qos.logback.classic.AsyncAppender">
    <appender-ref ref="file" />
    <queueSize>512</queueSize>
    <discardingThreshold>0</discardingThreshold>
</appender>

2. 日志文件管理

难点描述
  • 日志文件大小控制
  • 日志文件轮转策略
  • 日志文件清理策略
解决方案
<!-- 时间和大小双重控制的滚动策略 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
    <!-- 按日期滚动 -->
    <fileNamePattern>./logs/application.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
    <!-- 单个文件最大大小 -->
    <maxFileSize>100MB</maxFileSize>
    <!-- 保留天数 -->
    <maxHistory>30</maxHistory>
    <!-- 总大小限制 -->
    <totalSizeCap>3GB</totalSizeCap>
</rollingPolicy>

3. 分布式日志追踪

难点描述
  • 分布式系统中的请求追踪
  • 日志关联和聚合
  • 跨服务调用链追踪
解决方案
// 使用MDC存储追踪信息
@Component
public class LogTraceFilter implements Filter {
  
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
        try {
            // 生成追踪ID
            String traceId = UUID.randomUUID().toString();
            MDC.put("traceId", traceId);
          
            // 设置用户信息
            String userId = getUserId(request);
            MDC.put("userId", userId);
          
            chain.doFilter(request, response);
        } finally {
            // 清理MDC
            MDC.clear();
        }
    }
}
<!-- 日志格式包含MDC信息 -->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{traceId}] [%X{userId}] %-5level %logger{36} - %msg%n</pattern>

4. 日志安全控制

难点描述
  • 敏感信息泄露
  • 日志文件权限控制
  • 日志内容脱敏处理
解决方案
// 敏感信息脱敏工具类
@Component
public class LogMaskUtil {
  
    public static String maskPhone(String phone) {
        if (StringUtils.isEmpty(phone) || phone.length() < 7) {
            return phone;
        }
        return phone.substring(0, 3) + "****" + phone.substring(phone.length() - 4);
    }
  
    public static String maskIdCard(String idCard) {
        if (StringUtils.isEmpty(idCard) || idCard.length() < 10) {
            return idCard;
        }
        return idCard.substring(0, 6) + "********" + idCard.substring(idCard.length() - 4);
    }
}

// 使用脱敏工具
@Log4j2
public class UserService {
  
    public void createUser(User user) {
        // 脱敏后记录日志
        log.info("创建用户: 姓名={}, 手机={}, 身份证={}", 
            user.getName(), 
            LogMaskUtil.maskPhone(user.getPhone()),
            LogMaskUtil.maskIdCard(user.getIdCard()));
    }
}

最佳实践建议

1. 日志级别使用规范

// 推荐的日志级别使用
@Log4j2
public class BestPracticeService {
  
    public void processBusinessLogic() {
        // TRACE: 详细的执行步骤
        log.trace("进入方法 processBusinessLogic");
      
        // DEBUG: 调试信息,开发环境使用
        log.debug("处理业务逻辑,参数: {}", businessParam);
      
        // INFO: 重要的业务节点
        log.info("开始处理业务,业务ID: {}", businessId);
      
        try {
            // 业务逻辑处理
            if (log.isDebugEnabled()) {
                log.debug("业务处理中,当前状态: {}", currentStatus);
            }
          
            // 业务完成
            log.info("业务处理完成,结果: {}", result);
          
        } catch (BusinessException e) {
            // WARN: 业务异常,可恢复
            log.warn("业务处理警告,业务ID: {}, 原因: {}", businessId, e.getMessage());
            throw e;
          
        } catch (Exception e) {
            // ERROR: 系统异常,需要关注
            log.error("业务处理异常,业务ID: {}, 错误: {}", businessId, e.getMessage(), e);
            throw new RuntimeException("系统异常", e);
        }
    }
}

2. 日志配置优化

<!-- 生产环境推荐配置 -->
<configuration>
    <!-- 异步日志处理 -->
    <appender name="asyncFile" class="ch.qos.logback.classic.AsyncAppender">
        <appender-ref ref="file" />
        <queueSize>512</queueSize>
        <discardingThreshold>0</discardingThreshold>
        <includeCallerData>false</includeCallerData>
    </appender>
  
    <!-- 错误日志单独处理 -->
    <appender name="errorFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>./logs/error.log</file>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>./logs/error.%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>90</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>
  
    <!-- 根日志配置 -->
    <root level="info">
        <appender-ref ref="asyncFile" />
        <appender-ref ref="errorFile" />
    </root>
</configuration>

3. 日志监控和告警

# 日志监控配置
management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,loggers
  endpoint:
    loggers:
      enabled: true
    health:
      show-details: always

# 日志级别动态调整
logging:
  level:
    root: INFO
    com.zmy.enrollment: INFO
    # 可以通过Actuator动态调整
    # POST /actuator/loggers/com.zmy.enrollment
    # {"configuredLevel": "DEBUG"}

4. 日志分析工具集成

<!-- ELK集成配置 -->
<appender name="logstash" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
    <destination>localhost:5000</destination>
    <encoder class="net.logstash.logback.encoder.LogstashEncoder">
        <customFields>{"app_name":"enrollment-web","env":"prod"}</customFields>
    </encoder>
</appender>

<!-- 结构化日志输出 -->
<appender name="jsonFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>./logs/application.json</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <fileNamePattern>./logs/application.%d{yyyy-MM-dd}.json</fileNamePattern>
        <maxHistory>30</maxHistory>
    </rollingPolicy>
    <encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
        <providers>
            <timestamp/>
            <logLevel/>
            <loggerName/>
            <message/>
            <mdc/>
            <stackTrace/>
        </providers>
    </encoder>
</appender>

总结

本项目采用了完善的日志系统配置,具有以下特点:

1. 多层次日志配置

  • 控制台输出:开发调试使用
  • 文件输出:生产环境持久化
  • 追踪日志:性能监控和问题排查
  • 第三方日志:接口调用记录

2. 灵活的日志管理

  • 按业务模块分类
  • 按日志级别分类
  • 支持动态配置调整
  • 自动文件轮转和清理

3. 性能优化考虑

  • 异步日志处理
  • 条件日志输出
  • 合理的文件大小控制
  • 日志级别过滤

4. 运维友好设计

  • JSON格式输出,便于ELK分析
  • 包含追踪ID,支持分布式追踪
  • 结构化日志,便于监控告警
  • 多环境配置支持

这种日志配置方案既满足了开发调试的需求,又为生产环境的运维监控提供了强有力的支持,是一个值得参考的企业级日志配置实践。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值