← Back to blog
Guide 4 min read 炬鲸团队

Add OpenTelemetry Tracing to Spring Boot in Ten Minutes

Wire Spring Boot 3.x tracing into Jujing in ten minutes using Micrometer Tracing and an OpenTelemetry Collector. Includes dependencies, config, Collector setup, custom spans, and four common pitfalls.

What You Need

Three things are enough: a Spring Boot 3.x app (3.2+ ships Micrometer Tracing, which saves half the setup), an OpenTelemetry Collector (run one in Docker to receive and forward traces to Jujing), and your Jujing endpoint and token.

Step 1: Add Dependencies

Add two dependencies to Maven:

<dependency>
  <groupId>io.micrometer</groupId>
  <artifactId>micrometer-tracing-bridge-otel</artifactId>
</dependency>
<dependency>
  <groupId>io.opentelemetry</groupId>
  <artifactId>opentelemetry-exporter-otlp</artifactId>
</dependency>

Spring Boot 3.2+ auto-configures tracing through the actuator, so you don't instantiate a Tracer by hand or write span-export logic yourself.

Step 2: Configure application.yml

spring:
  application:
    name: order-service
management:
  tracing:
    sampling:
      probability: 1.0
  otlp:
    tracing:
      endpoint: http://localhost:4318/v1/traces

spring.application.name becomes the service.name automatically, which is what you filter by when debugging. probability: 1.0 means 100% sampling — fine locally, but drop it to 0.1–0.3 in production. The endpoint points at the Collector's HTTP port 4318.

Step 3: Start the Collector and Verify

otel-collector-config.yaml:

receivers:
  otlp:
    protocols:
      http:
        endpoint: 0.0.0.0:4318
exporters:
  otlphttp:
    endpoint: https://ob.jjhub.cn/v1/traces
    headers:
      Authorization: Bearer <token>
service:
  pipelines:
    traces:
      receivers: [otlp]
      exporters: [otlphttp]

Start it: docker run -v $PWD/otel-collector-config.yaml:/etc/otelcol/config.yaml otel/opentelemetry-collector-contrib

Hit your endpoints a few times, open the Trace list in Jujing, filter by service.name, and you'll see the full call chain. Click a span to inspect its duration, status code, and custom attributes.

Custom Spans and Going Further

Framework instrumentation covers HTTP, databases, and messaging, but business-critical steps need manual spans. Inject the Tracer and wrap key methods like placing an order or deducting inventory:

Span span = tracer.nextSpan().name("place_order").start();
try (var scope = tracer.withSpan(span)) {
    span.setTag("txn_id", txnId);
    // business logic
} finally {
    span.end();
}

In production, offload sampling to the Collector with tail-based sampling: keep every trace slower than 500ms or with errors, sample the rest by ratio. That protects storage without missing the traces that matter.

Common Pitfalls

  • No data at all: check the port — 4318 is HTTP, 4317 is gRPC; both ends must agree.
  • Only single-span traces: the traceparent header isn't being forwarded downstream. Make sure RestTemplate/WebClient use the auto-configured versions and headers propagate across services.
  • service.name shows "unknown": you forgot spring.application.name.
  • Sampling overloads storage: 1.0 is fine locally; start production at 0.1 and watch storage growth before raising it.