You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot应用OpenTelemetry结构化JSON日志问题及实现咨询

Spring Boot + OpenTelemetry 结构化日志解决方案(适配Loki)

一、修复OpenTelemetry JSON结构化日志输出

1. 依赖配置

添加OpenTelemetry JSON日志导出器和Logback适配依赖(以Maven为例):

<dependency>
    <groupId>io.opentelemetry</groupId>
    <artifactId>opentelemetry-exporter-logging-json</artifactId>
    <version>1.32.0</version> <!-- 建议使用最新稳定版本 -->
</dependency>
<dependency>
    <groupId>io.opentelemetry.instrumentation</groupId>
    <artifactId>opentelemetry-logback-appender-1.0</artifactId>
    <version>1.32.0</version>
</dependency>

2. Logback配置

修改logback-spring.xml,替换默认控制台输出为OpenTelemetry JSON Appender:

<configuration>
    <!-- OpenTelemetry JSON日志输出Appender -->
    <appender name="OTEL-JSON" class="io.opentelemetry.instrumentation.logback.appender.v1_0.OpenTelemetryAppender">
        <encoder class="io.opentelemetry.exporter.logging.json.JsonLogEncoder"/>
    </appender>

    <!-- 根日志级别配置 -->
    <root level="INFO">
        <appender-ref ref="OTEL-JSON"/>
    </root>
</configuration>

3. OpenTelemetry基础配置

在application.yml中指定日志导出器和服务标识:

opentelemetry:
  logs:
    exporter: logging
  resource:
    attributes:
      service.name: your-service-name # 替换为你的服务名称

二、请求/响应/请求头的结构化日志采集

方式1:基于Servlet Instrumentation自动采集

通过OpenTelemetry Servlet工具包自动捕获请求上下文,同时自定义Filter补充请求头、响应信息:

添加依赖

<dependency>
    <groupId>io.opentelemetry.instrumentation</groupId>
    <artifactId>opentelemetry-servlet-3.0</artifactId>
    <version>1.32.0</version>
    <scope>runtime</scope>
</dependency>

自定义日志Filter

创建Filter捕获请求头、响应状态等信息,并附加到OpenTelemetry Span属性中(最终会被结构化日志包含):

import io.opentelemetry.api.trace.Span;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;

public class RequestResponseLoggingFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse res = (HttpServletResponse) response;

        // 绑定当前请求的Span
        Span currentSpan = Span.current();
        // 追加请求属性
        currentSpan.setAttribute("request.method", req.getMethod());
        currentSpan.setAttribute("request.path", req.getRequestURI());
        currentSpan.setAttribute("request.user_agent", req.getHeader("User-Agent"));
        currentSpan.setAttribute("request.content_type", req.getHeader("Content-Type"));
        // 按需添加其他需要采集的请求头

        chain.doFilter(request, response);

        // 追加响应属性
        currentSpan.setAttribute("response.status_code", res.getStatus());
        currentSpan.setAttribute("response.content_length", res.getContentLength());
    }
}

注册Filter

在Spring配置类中注册该Filter:

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FilterConfig {
    @Bean
    public FilterRegistrationBean<RequestResponseLoggingFilter> requestResponseLoggingFilter() {
        FilterRegistrationBean<RequestResponseLoggingFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new RequestResponseLoggingFilter());
        registrationBean.addUrlPatterns("/*"); // 匹配所有请求路径
        return registrationBean;
    }
}

方式2:手动构建结构化日志事件

如果需要更灵活的日志内容控制,直接使用OpenTelemetry Logger API手动创建包含请求/响应信息的JSON日志:

import io.opentelemetry.api.logs.Logger;
import io.opentelemetry.api.logs.LoggerProvider;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

@Component
public class RequestResponseLogger {
    private final Logger logger;

    public RequestResponseLogger(LoggerProvider loggerProvider) {
        this.logger = loggerProvider.getLogger("RequestResponseLogger");
    }

    public void logRequestDetails(HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> logAttributes = new HashMap<>();
        logAttributes.put("request.method", request.getMethod());
        logAttributes.put("request.path", request.getRequestURI());
        logAttributes.put("request.user_agent", request.getHeader("User-Agent"));
        logAttributes.put("response.status_code", response.getStatus());
        // 按需添加其他字段

        logger.logBuilder()
                .setBody("Request processed successfully")
                .addAttributes(logAttributes)
                .emit();
    }
}

之后可在Controller、Interceptor或Filter中调用该Logger输出日志。

三、适配Loki与Grafana可视化

Loki原生支持JSON日志解析,只需在Promtail采集配置中开启JSON解析规则,即可将结构化日志字段转为Loki标签或可查询字段:

修改promtail.yml的采集配置:

scrape_configs:
  - job_name: "spring-boot-service"
    static_configs:
      - targets: ["localhost"]
        labels:
          job: "spring-boot-service"
          __path__: "/var/log/your-service/*.log" # 替换为你的服务日志路径
    pipeline_stages:
      # 解析JSON日志字段
      - json:
          expressions:
            timestamp: timestamp
            message: body
            service_name: service.name
            request_method: attributes.request.method
            request_path: attributes.request.path
            response_status: attributes.response.status_code
      # 将关键字段转为Loki标签
      - labels:
          service_name:
          request_method:
          response_status:
      # 配置时间戳解析
      - timestamp:
          source: timestamp
          format: RFC3339Nano

配置完成后,在Grafana中即可通过Loki数据源,基于标签和字段进行日志查询、过滤与可视化。

内容的提问来源于stack exchange,提问作者Senthilkumar Dhanapal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 22:44:59