Java OpenTelemetry 监控实战:Spring Boot 指标接入完整教程

为 Java/Spring Boot 应用接入 OpenTelemetry 指标:依赖引入、Counter/Gauge/Histogram 实现、用属性为指标添加上下文维度、外部 API 调用监控,以及导出到观测云实现看板与告警。附 Filter 与 Service 完整代码。

最佳实践
Java OpenTelemetry 监控实战:Spring Boot 指标接入完整教程封面

Java 的 OpenTelemetry 指标接入有两种路线:零代码的 Java Agent(自动埋点主流框架)与显式的 SDK 编程(完全掌控业务指标)——生产实践通常是两者结合:Agent 兜底框架层,手写代码覆盖核心业务指标。

核心要点速览

  • Spring Boot 场景用 Filter/拦截器统一埋点 HTTP 指标;
  • Counter 记总量、Gauge 记水位、Histogram 记延迟分布;
  • Attributes 为指标加维度(端点/方法/状态码),但要防高基数;
  • 导出端点指向观测云 DataKit,指标告警看板一站搞定。

引入依赖

<dependency>
    <groupId>io.opentelemetry</groupId>
    <artifactId>opentelemetry-api</artifactId>
</dependency>
<dependency>
    <groupId>io.opentelemetry</groupId>
    <artifactId>opentelemetry-sdk</artifactId>
</dependency>
<dependency>
    <groupId>io.opentelemetry</groupId>
    <artifactId>opentelemetry-exporter-otlp</artifactId>
</dependency>

或者更简单——直接用 Java Agent(-javaagent:opentelemetry-javaagent.jar),通过环境变量配置 OTLP 端点,HTTP/数据库/消息队列指标自动产出。

封装指标 Service

@Service
public class MetricsService {
    private final LongCounter requestCounter;
    private final LongUpDownCounter activeRequests;
    private final DoubleHistogram requestDuration;

    public MetricsService(MeterProvider meterProvider) {
        Meter meter = meterProvider.get("shop-service");
        this.requestCounter = meter.counterBuilder("http.server.requests.total")
            .setDescription("Total HTTP requests").setUnit("requests").build();
        this.activeRequests = meter.upDownCounterBuilder("http.server.active_requests")
            .setDescription("Active HTTP requests").build();
        this.requestDuration = meter.histogramBuilder("http.server.request.duration")
            .setDescription("Request latency").setUnit("s").build();
    }

    public void incrementRequestCounter(Attributes attrs) { requestCounter.add(1, attrs); }
    public void incrementActiveRequests(Attributes attrs) { activeRequests.add(1, attrs); }
    public void decrementActiveRequests(Attributes attrs) { activeRequests.add(-1, attrs); }
    public void recordDuration(double seconds, Attributes attrs) { requestDuration.record(seconds, attrs); }
}

用 Filter 统一埋点并附加属性

@Component
public class MetricsFilter extends OncePerRequestFilter {
    private final MetricsService metricsService;

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain chain) throws ServletException, IOException {
        Attributes attrs = Attributes.builder()
            .put("endpoint", request.getRequestURI())
            .put("method", request.getMethod())
            .build();
        metricsService.incrementRequestCounter(attrs);
        metricsService.incrementActiveRequests(attrs);
        long start = System.nanoTime();
        try {
            chain.doFilter(request, response);
        } finally {
            metricsService.decrementActiveRequests(attrs);
            metricsService.recordDuration(
                (System.nanoTime() - start) / 1e9,
                attrs.toBuilder().put("status", response.getStatus()).build());
        }
    }
}

基数警告request.getRequestURI() 若含路径参数(/user/12345)会让每个用户一个标签组合——务必改用路由模板(Spring 的 HandlerMapping 最佳匹配模式),把维度数控制在几十个以内。

监控外部 API 调用

对下游依赖单独建指标,出问题时分得清"是我慢还是它慢":

public Product fetchProduct(String id) {
    long start = System.nanoTime();
    try {
        Product p = restTemplate.getForObject("/products/" + id, Product.class);
        productApiCalls.add(1, Attributes.builder()
            .put("api", "product-service").put("result", "success").build());
        return p;
    } catch (Exception e) {
        productApiCalls.add(1, Attributes.builder()
            .put("api", "product-service").put("result", "failure").build());
        throw e;
    } finally {
        productApiDuration.record((System.nanoTime() - start) / 1e9,
            Attributes.of(AttributeKey.stringKey("api"), "product-service"));
    }
}

Java Agent 与手写指标的分工

推荐手段
HTTP/JDBC/Redis/MQ 框架指标 Java Agent 自动埋点
JVM 运行时(GC/堆/线程) Agent 自带
业务指标(订单量、转化率) 手写 SDK 代码

Agent 的 OTLP 端点用环境变量配置:OTEL_EXPORTER_OTLP_ENDPOINT=http://datakit:4318

观测云落地:Java 应用的完整监控栈

OTLP 端点指向观测云 DataKit 后,指标进入观测云指标模块——用 DQL 按 endpoint 聚合出各接口的 QPS/错误率/P95,场景视图搭建服务总览看板,监控器配置"active_requests 突增""外部 API 失败率"等告警直达钉钉/企微/飞书。Java Agent 同时会产出链路数据,观测云 APM 中的服务拓扑、火焰图开箱即有,指标异常一键下钻到具体 Trace。

常见问题(FAQ)

Q:有了 Micrometer 还需要 OTel 指标吗? Micrometer 是门面、OTel 可以是其实现之一;新项目直接用 OTel API 更简单,存量 Micrometer 代码可通过桥接迁移。

Q:Agent 和 SDK 同时用会重复上报吗? 框架层指标由 Agent 产出、业务指标由手写代码产出,名称空间不同不冲突;注意别让手写代码重复统计 Agent 已覆盖的 HTTP 指标。

Q:Histogram 桶边界怎么定? 按 SLO 定:如接口要求 P99 < 1s,桶边界围绕 0.1/0.3/0.5/1/2s 分布,让关键区间分辨率足够。

Q:多实例部署指标怎么区分? OTel 资源属性自动带 service.instance.id,观测云中按实例标签过滤或聚合即可。

系列阅读

获取专属方案

联系我们

加入社区

微信扫码
加入官方交流群

立即体验

在线开通,按量计费,真正的云服务!

立即开始

选择观测云版本

代码托管平台