SpringBoot实现日志系统的完整代码

好的,在 Spring Boot 中实现一个完整、灵活且生产可用的日志系统,不仅仅是引入依赖那么简单。它需要结合 Spring Boot 的约定、日志框架的特性以及良好的实践。

本文将提供一个从基础配置到高级定制的完整指南,包含完整的代码示例和解释。

我们将使用 ​SLF4J​ 作为门面(Facade),其默认实现为 ​Logback,因为 Spring Boot 的 starter 已经为我们默认集成了它们,无需额外引入依赖。

图片[1]_SpringBoot实现日志系统的完整代码_知途无界

1. 基础:Spring Boot 默认日志配置

Spring Boot 默认使用 Logback,并通过 spring-boot-starter-logging 自动配置。你无需做任何事即可开始使用日志。

1.1 最简单的日志记录

在任何 Spring Bean(如 @Component, @Service, @Controller)中,你都可以直接注入 Logger 并使用。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

@Service
public class MyService {

    // 1. 定义一个静态的 Logger 对象,参数为当前类的 Class
    private static final Logger logger = LoggerFactory.getLogger(MyService.class);

    public void doSomething() {
        // 2. 使用不同级别的方法记录日志
        logger.trace("This is a TRACE level message.");
        logger.debug("This is a DEBUG level message.");
        logger.info("This is an INFO level message."); // 默认级别
        logger.warn("This is a WARN level message.");
        logger.error("This is an ERROR level message.");

        String userName = "Alice";
        Integer userId = 123;
        // 3. 使用占位符 {} 来避免字符串拼接,性能更好
        logger.info("User {} with ID {} has logged in.", userName, userId);

        try {
            // Some risky operation
            1 / 0;
        } catch (Exception e) {
            // 4. 记录异常堆栈信息,第二个参数是 Throwable
            logger.error("An unexpected error occurred while processing the request.", e);
        }
    }
}

1.2 日志级别(Log Level)​

  • TRACE​ < ​DEBUG​ < ​INFO​ < ​WARN​ < ​ERROR​ < ​FATAL​ (SLF4J/Logback 无 FATAL)
  • Spring Boot 默认日志级别是 ​INFO。这意味着 DEBUGTRACE 级别的日志不会被打印。
  • 你可以在 application.properties 中轻松调整特定包的日志级别。

2. 核心配置:application.properties / application.yml

这是最常用的配置方式,适合大多数场景。

2.1 配置日志级别

# application.properties

# 设置根日志级别为 INFO
logging.level.root=INFO

# 设置特定包的日志级别为 DEBUG,便于调试
logging.level.com.yourcompany.yourapp.service=DEBUG
logging.level.org.springframework.web=INFO
logging.level.org.hibernate.SQL=DEBUG # 打印 Hibernate 生成的 SQL 语句
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE # 打印 SQL 参数

# 设置特定类的日志级别
logging.level.com.yourcompany.yourapp.SomeImportantClass=TRACE
# application.yml

logging:
  level:
    root: INFO
    com.yourcompany.yourapp.service: DEBUG
    org.springframework.web: INFO
    org.hibernate.SQL: DEBUG
    org.hibernate.type.descriptor.sql.BasicBinder: TRACE

2.2 配置日志输出

# application.properties

# 日志文件输出
logging.file.name=logs/my-app.log # 指定日志文件名,会在项目根目录下创建 logs 文件夹
# 或者
logging.file.path=./logs # 指定日志目录,默认文件名为 spring.log

# 日志格式
# 控制台日志格式
logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n

# 文件日志格式
logging.pattern.file=%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n

# 日志文件大小与归档策略
# 单个文件最大 10MB,最多保留 30 个归档文件,总大小约 300MB
logging.logback.rollingpolicy.max-file-size=10MB
logging.logback.rollingpolicy.max-history=30
logging.logback.rollingpolicy.total-size-cap=300MB
# 使用基于时间的归档策略(例如:my-app-2023-10-27.0.gz)
logging.logback.rollingpolicy.file-name-pattern=logs/my-app-%d{yyyy-MM-dd}.%i.gz

3. 高级定制:使用 logback-spring.xml

application.properties 的配置无法满足需求时(例如:更复杂的归档策略、不同的环境使用不同的配置、异步日志等),我们需要创建 Logback 的 XML 配置文件。

命名约定​:logback-spring.xml (推荐) 优于 logback.xml。因为 logback-spring.xml 可以利用 Spring Boot 的 profile 功能,实现环境隔离。

文件位置​:src/main/resources/logback-spring.xml

3.1 完整的 logback-spring.xml 示例

这个配置文件包含了环境隔离、按天滚动、异步日志等高级特性。

<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="30 seconds">

    <!-- 1. 定义变量 -->
    <property name="LOG_PATH" value="./logs" />
    <property name="LOG_FILE" value="my-app" />
    <property name="LOG_CHARSET" value="UTF-8" />

    <!-- 2. 定义 Appender (输出目的地) -->

    <!-- 2.1 控制台 Appender -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!-- 使用 Spring Boot 风格的默认格式 -->
            <pattern>${CONSOLE_LOG_PATTERN:-%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}</pattern>
            <charset>${LOG_CHARSET}</charset>
        </encoder>
        <!-- 过滤器,只输出 INFO 及以上级别到控制台 -->
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>INFO</level>
        </filter>
    </appender>

    <!-- 2.2 滚动文件 Appender (INFO 级别) -->
    <appender name="FILE_INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/${LOG_FILE}-info.log</file>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
            <charset>${LOG_CHARSET}</charset>
        </encoder>
        <!-- 基于时间和大小的滚动策略 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/archive/${LOG_FILE}-info.%d{yyyy-MM-dd}.%i.gz</fileNamePattern>
            <maxFileSize>10MB</maxFileSize>
            <maxHistory>30</maxHistory>
            <totalSizeCap>1GB</totalSizeCap>
        </rollingPolicy>
        <!-- 过滤器,只接受 INFO 级别的日志 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFO</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!-- 2.3 滚动文件 Appender (ERROR 级别) -->
    <appender name="FILE_ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/${LOG_FILE}-error.log</file>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
            <charset>${LOG_CHARSET}</charset>
        </encoder>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/archive/${LOG_FILE}-error.%d{yyyy-MM-dd}.%i.gz</fileNamePattern>
            <maxFileSize>10MB</maxFileSize>
            <maxHistory>30</maxHistory>
            <totalSizeCap>1GB</totalSizeCap>
        </rollingPolicy>
        <!-- 过滤器,只接受 ERROR 级别的日志 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!-- 2.4 异步 Appender (提高性能) -->
    <!-- 注意:AsyncAppender 应包装在其他 appender 外面 -->
    <appender name="ASYNC_FILE_INFO" class="ch.qos.logback.classic.AsyncAppender">
        <queueSize>512</queueSize> <!-- 队列大小 -->
        <discardingThreshold>0</discardingThreshold> <!-- 不丢失任何日志 -->
        <appender-ref ref="FILE_INFO" />
    </appender>

    <appender name="ASYNC_FILE_ERROR" class="ch.qos.logback.classic.AsyncAppender">
        <queueSize>512</queueSize>
        <discardingThreshold>0</discardingThreshold>
        <appender-ref ref="FILE_ERROR" />
    </appender>


    <!-- 3. 根据 Spring Profile 激活不同的配置 -->
    <springProfile name="dev">
        <!-- 开发环境:只输出到控制台 -->
        <root level="DEBUG">
            <appender-ref ref="CONSOLE" />
        </root>
    </springProfile>

    <springProfile name="prod">
        <!-- 生产环境:输出到文件和控制台,使用异步 -->
        <root level="INFO">
            <appender-ref ref="CONSOLE" />
            <appender-ref ref="ASYNC_FILE_INFO" />
            <appender-ref ref="ASYNC_FILE_ERROR" />
        </root>
    </springProfile>

    <!-- 4. 可以为特定包或类单独设置级别和 Appender -->
    <logger name="com.yourcompany.yourapp.repository" level="DEBUG" additivity="false">
        <appender-ref ref="CONSOLE"/>
    </logger>

</configuration>

3.2 关键点解释

  • ​**<configuration scan="true">**​:允许 Logback 在运行时扫描配置文件的变化并自动重新加载。
  • ​**<springProfile>**​:这是 logback-spring.xml 独有的功能。只有当 spring.profiles.active 匹配时,里面的配置才会生效。例如,在 application-prod.properties 中设置 spring.profiles.active=prod
  • Appender Filter​:使用 LevelFilter 可以精确控制 Appender 接收哪个级别的日志。ThresholdFilter 则接收一个阈值以上的所有级别。
  • 异步 Appender (AsyncAppender)​​:将日志事件放入一个队列,由另一个线程异步地进行 I/O 操作,极大地提升了应用性能,特别是在高并发场景下。
  • ​**<additivity="false">**​:阻止日志事件向上级(root logger)传递,避免重复打印。

4. 最佳实践与建议

  1. 使用 SLF4J 门面​:不要在代码中直接使用 Logback 的类(如 ch.qos.logback.classic.Logger),始终使用 org.slf4j.LoggerLoggerFactory。这保证了日志实现的灵活性。
  2. ​**使用占位符 {}**​:避免字符串拼接,如 logger.debug("User " + name),因为当日志级别不满足时,字符串拼接操作依然会发生。使用 logger.debug("User {}", name) 则不会。
  3. 合理的日志级别​:
    • ERROR: 系统异常、影响核心业务流程的错误。
    • WARN: 不影响主流程的警告,如重试、降级、参数校验失败等。
    • INFO: 重要的业务流程节点、服务启停、用户登录登出等。
    • DEBUG: 详细的调试信息,如方法的输入输出、SQL 执行详情。
    • TRACE: 最详细的跟踪信息,如循环内部的每一步。
  4. 生产环境配置​:
    • 使用 logback-spring.xml 进行精细化配置。
    • 务必配置日志文件滚动,防止磁盘被写满。
    • 考虑使用异步日志 (AsyncAppender) 提升性能。
    • 将不同级别的日志分离到不同文件,便于问题排查(如只看 error.log)。
    • 定期监控日志文件大小和磁盘空间。
  5. 安全​:切勿在日志中记录敏感信息,如密码、身份证号、银行卡号等。

通过以上步骤,你就可以在 Spring Boot 中构建一个强大、灵活且符合生产标准的日志系统。

© 版权声明
THE END
喜欢就点个赞,支持一下吧!
点赞10 分享
评论 抢沙发
头像
欢迎您留下评论!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容