基于 Logstash Logback Encoder 的 Spring Boot 日志记录

内容纲要

基于 Logstash Logback Encoder 的 Spring Boot 日志记录

在本教程中,我们将详细介绍如何使用 Logstash Logback Encoder 实现 Spring Boot 的结构化日志记录。我们将在环境中使用Spring Boot 2.7, Logstash Logback Encoder 6.6,以及 JDK 11。

1. 配置 Maven 依赖

开始之前,我们需要在项目的 pom 文件中添加 Logstash Logback Encoder 的依赖:

<dependencies>
    <dependency>
        <groupId>net.logstash.logback</groupId>
        <artifactId>logstash-logback-encoder</artifactId>
        <version>6.6</version>
    </dependency>
</dependencies>

2. 自定义 MDC

为了把特定的上下文信息(比如用户ID或者请求ID)添加到所有的日志条目中,我们可以通过实现 HandlerInterceptor 在 Spring Boot 中自定义 Mapped Diagnostic Context (MDC):

@Component
public class MdcInterceptor extends HandlerInterceptorAdapter {

    private static final String MDC_USER_CODE = "MDC_user_code";

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
      MDC.put(MDC_USER_CODE, computeUserCode());
      return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
      MDC.remove(MDC_USER_CODE);
    }

    private String computeUserCode() {
      // 在这里,我们只是简单地返回一个固定的User Code以示范。
      // 在实际应用中,这个User Code可能来自于请求的头信息,Cookie,或者是其他的上下文信息。
      return "userCode";
    }
}

注册Interceptor到InterceptorRegistry

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration(proxyBeanMethods = false)
public class WebMvcConfig implements WebMvcConfigurer {

    @Autowired
    private MDCInterceptor mdcInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(mdcInterceptor).addPathPatterns("/**");
    }
}

3. 设置 Logback 配置

最后一步是配置我们的 logback-spring.xml 文件,来使用我们刚刚创建的MDC以及 Logstash Logback Encoder:

<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="10 seconds">
  <appender name="STOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
      <providers>
        <timestamp>
          <timeZone>UTC</timeZone>
        </timestamp>
        <pattern>
          <pattern>
            {
                "timestamp": "%date{\"yyyy-MM-dd'T'HH:mm:ss,SSSZ\"}",
                "log_level": "%level",
                "thread": "%thread",
                "class_name": "%class",
                "line_number": "%line",
                <!-- 这里是MDC中自定义的字段和内容 -->
                "MDC_user_code": "%X{MDC_user_code}",
                "message": "%message",
                "stack_trace": "%exception"
            }
          </pattern>
        </pattern>
        <!-- 使用LoggingEventCompositeJsonEncoder时不需要设置mdc,因为设置了mdc可能会导致日志中自定义字段重复 -->
        <!-- <mdc/> -->
        <stackTrace/>
      </providers>
    </encoder>
  </appender>
  <root level="DEBUG">
    <appender-ref ref="STOUT"/>
  </root>
</configuration>

在这个配置文件中,我们设置了 Logstash Logback Encoder 输出的日志格式,并配置了一个 appender 将日志输出到控制台。

现在,每当你使用 Logger 进行日志记录时,如 log.info("This is my MDC test"),你会看到以下结构化的日志输出:

{
  "timestamp": "2023-10-18T12:44:22,084+0800",
  "log_level": "INFO",
  "thread": "http-nio-8080-exec-2",
  "class_name": "com.example.sales.product.client.app.OrderController",
  "line_number": "22",
  "MDC_user_code": "399ed0d8-842f-4f40-bcfa-8af897cb2823",
  "message": "This is my MDC test",
  "stack_trace": ""
}

这样,我们就实现了 Spring Boot 中的 JSON 格式化并具有用户上下文信息的日志记录。以此来提高日志系统的可读性和可用性。

4. 注意事项

以下是使用 Logstash Logback Encoder 和 Spring Boot 进行日志记录时需要注意的一些要点:

  1. 配置正确: 确保 Maven 依赖和logback-spring.xml配置文件设置正确。任何配置错误都会导致日志记录不成功。

  2. 清理 MDC: MDC 是线程相关的,因此在每次请求结束后,需要清理 MDC以防止信息在多个请求之间泄露。在上述示例中,我们在afterCompletion方法中调用了MDC.remove来完成清理工作。

  3. 确认 MDC 的值: 计算 MDC 的值时要尽量确保这些值的有效性和正确性。MDC值的错误可能会导致日志分析的困难,所以获取 MDC 值时务必谨慎。

  4. 性能考虑: 虽然结构化日志对日志分析和故障排查非常有帮助,但它也比普通文本日志更消耗性能。如果你的应用需要产生大量的日志且对性能有苛刻的要求,那么需要在使用结构化日志之前对其可能带来的性能影响有充分的了解。

  5. 日志级别: 过多的日志可能会给存储和分析带来压力,因此需要根据实际情况合理设置日志级别。一般来说,生产环境中我们只记录INFO级别以及以上级别的日志(WARNERROR)。在开发和测试环境,由于需要更详细的日志来调试和排查问题,我们可能会记录DEBUG级别甚至TRACE级别的日志。

  6. 当心敏感信息: 日志可能会被错误地用作传输和存储敏感信息,如密码、秘钥和用户私人数据。要确保这些信息不会出现在你的日志中。

发表评论

您的电子邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部