Java Fundamentals Tutorial 0/33 lessons ~6 min read Lesson 30

    OpenTelemetry

    OpenTelemetry (OTel) is the vendor-neutral standard for tracing, metrics, and logs — replacing fragmented agents (Jaeger client, Prometheus client, custom MDC) with one SDK and…

    Course progress0%
    Focus
    21 guided sections
    Practice signal
    Examples included
    Career prep
    Interview Q&A included

    Introduction

    OpenTelemetry (OTel) is the vendor-neutral standard for tracing, metrics, and logs — replacing fragmented agents (Jaeger client, Prometheus client, custom MDC) with one SDK and OTLP export. Distributed tracing propagates trace context across Spring Boot services, Kafka consumers, and JDBC calls — one trace ID follows a payment request from gateway to database.

    Java integration: OpenTelemetry Java agent (zero-code attach) or Spring Boot 3 Micrometer OTel bridge. Export to Grafana Tempo, Jaeger, Datadog, or Honeycomb via OTLP. Staff engineers mandate trace propagation before microservice count exceeds five — otherwise incidents are blind.

    Business problem

    Operating microservices without observability:

    • Blind incidents: Payment timeout — which of 8 services slow? Hours of log grep.
    • No correlation: Separate trace ID, request ID, log MDC — can't link metrics spike to failing traces.
    • Vendor lock-in: Datadog agent + Jaeger + Prometheus — three SDKs, three configs.
    • Missing SLO data: No histogram of checkout latency — guessing p99 from averages.
    • Kafka black box: Consumer lag visible but which message processing fails? — need span per consume.

    Why this topic exists

    OpenTelemetry unifies the three pillars with one context propagation model:

    • Traces: Spans with parent-child hierarchy — visualize request waterfall across services.
    • Metrics: Counters, gauges, histograms — RED (Rate, Errors, Duration) per service.
    • Logs: Structured logs with trace_id injected — click trace → see logs; click log → see trace.
    • Context propagation: W3C traceparent header — gateway → order → payment → Kafka consumer.
    • OTLP: Single export protocol to any backend — switch vendor without re-instrumenting app.

    Core concepts

    OpenTelemetry concepts for Java:

    • Tracer: Creates spans — span = tracer.spanBuilder("processPayment").startSpan().
    • Span: Named operation with start/end time, attributes (http.status_code=200), events.
    • Trace: Tree of spans sharing trace_id — root span from gateway, child spans per service call.
    • Context propagation: Inject traceparent into HTTP headers and Kafka record headers on produce; extract on consume.
    • Meter: Records metrics — counter.add(1, attributes) for orders_processed_total.
    • Auto-instrumentation: Java agent instruments Spring MVC, JDBC, Kafka, HttpClient without code changes.

    Internal architecture

    Distributed trace across Java microservices:

    text
    Client POST /checkout
    ▼ trace_id=abc123 span_id=root
    ┌─────────────┐
    │ API Gateway │ span: HTTP POST /checkout (120ms)
    └──────┬──────┘
    │ traceparent: 00-abc123-root-01
    ┌─────────────┐
    │ Order Svc │ span: createOrder (80ms)
    │ │ ├─ span: JDBC insert (15ms)
    │ │ └─ span: kafka send order.events (5ms)
    └──────┬──────┘
    │ Kafka header: traceparent
    ┌─────────────┐
    │ Inventory │ span: consume OrderCreated (40ms)
    └─────────────┘
    OTLP export → OpenTelemetry Collector → Tempo (traces)
    → Prometheus (metrics)
    → Loki (logs via trace_id)

    Tracing, metrics, logs, and propagation:

    Trace span hierarchy
    Root span
    Gateway HTTP
    Child span
    Order service
    Grandchild
    JDBC query
    trace_id
    Same across all
    Waterfall view shows where latency accumulates — JDBC 15ms vs HTTP 80ms.
    W3C trace propagation
    traceparent
    HTTP header
    Inject
    Outgoing call
    Extract
    Incoming request
    Kafka hdr
    Record headers
    Broken propagation = orphaned spans — new trace_id per service, useless waterfall.
    Three pillars unified
    Traces
    Latency path
    Metrics
    Aggregates RED
    Logs
    trace_id field
    OTLP
    Single export
    Correlate spike in error rate metric to example trace and its logs.

    Code walkthrough

    Manual OpenTelemetry spans in Java + Spring Boot 3 Micrometer:

    • try (Scope scope): Makes span current — child HTTP/JDBC auto-instrumentation nests under it.
    • setAttribute: Searchable facets in trace UI — filter by payment.currency=USD.
    • recordException: Stack trace attached to span — visible in Jaeger/Tempo.
    • sampling.probability: 10% in prod reduces cost — always sample errors if tail-based sampling configured.
    java
    // build.gradle — Spring Boot 3.2+
    dependencies {
    implementation("io.opentelemetry:opentelemetry-api:1.36.0")
    implementation("org.springframework.boot:spring-boot-starter-actuator")
    implementation("io.micrometer:micrometer-tracing-bridge-otel")
    implementation("io.opentelemetry:opentelemetry-exporter-otlp")
    }
    // Manual span in payment logic
    @Service
    public class PaymentService {
    private final Tracer tracer = GlobalOpenTelemetry.getTracer("payment-service");
    public PaymentResult charge(PaymentRequest req) {
    Span span = tracer.spanBuilder("charge")
    .setSpanKind(SpanKind.SERVER)
    .startSpan();
    try (Scope scope = span.makeCurrent()) {
    span.setAttribute("payment.amount", req.amount());
    span.setAttribute("payment.currency", req.currency());
    validate(req);
    PaymentResult result = gateway.charge(req);
    span.setStatus(StatusCode.OK);
    return result;
    } catch (Exception e) {
    span.recordException(e);
    span.setStatus(StatusCode.ERROR, e.getMessage());
    throw e;
    } finally {
    span.end();
    }
    }
    }
    // application.yml — OTLP export
    management:
    tracing:
    sampling:
    probability: 1.0 # 100% dev; 0.1 prod
    otlp:
    tracing:
    endpoint: http://otel-collector:4318/v1/traces
    metrics:
    export:
    otlp:
    enabled: true

    Production example

    Java agent auto-instrumentation + Kafka propagation:

    • -javaagent: Zero-code instrumentation for Spring, JDBC, Kafka, RestTemplate — fastest adoption path.
    • OTEL_SERVICE_NAME: Appears in every span — filter traces by service in UI.
    • Kafka inject/extract: Consumer continues same trace — end-to-end checkout visibility.
    • traceId in logs: MDC populated by Micrometer bridge — correlate logs to traces in Grafana.
    java
    # Dockerfile — attach OpenTelemetry Java agent
    ENV OTEL_SERVICE_NAME=payment-service
    ENV OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317
    ENV OTEL_TRACES_EXPORTER=otlp
    ENV OTEL_METRICS_EXPORTER=otlp
    ENV OTEL_INSTRUMENTATION_KAFKA_ENABLED=true
    ENTRYPOINT ["java", "-javaagent:/otel/opentelemetry-javaagent.jar", \
    "-jar", "/app.jar"]
    # Kafka producer — propagate trace context in headers (agent does auto)
    # Manual if needed:
    public ProducerRecord<String, OrderEvent> withTrace(OrderEvent event) {
    ProducerRecord<String, OrderEvent> record =
    new ProducerRecord<>("order.events", event.orderId(), event);
    GlobalOpenTelemetry.getPropagators().getTextMapPropagator()
    .inject(Context.current(), record, (carrier, key, value) ->
    carrier.headers().add(key, value.getBytes(StandardCharsets.UTF_8)));
    return record;
    }
    # Grafana dashboard — RED metrics from Micrometer
    # rate(http_server_requests_seconds_count{service="payment"})
    # histogram_quantile(0.99, rate(http_server_requests_seconds_bucket[5m]))
    # Structured logging with trace_id (Logback)
    # logging.pattern.level=%5p [traceId=%X{traceId} spanId=%X{spanId}]

    Enterprise case study

    Stripe — observability culture: Stripe invests heavily in distributed tracing for payment flows — every API request traceable end-to-end. Java teams adopting OTel mirror this: sampling strategy is critical — 100% traces at Stripe-scale is petabytes; use probabilistic sampling (1–10%) plus tail-based sampling (keep all errors and slow traces). Lesson: define SLO on trace-derived metrics — p99 checkout span duration < 500ms.

    • Before: Logs only — MTTR 2 hours for cross-service timeout incidents.
    • After: OTel traces — identify slow JDBC in inventory service in 5 minutes.
    • Java agent rollout: Attach agent via ENV in Deployment — no code change first sprint.
    • Cost control: 10% head sampling + collector tail sampling for status=ERROR.

    Performance considerations

    OTel performance impact on Java:

    • Sampling: Head-based 10% sampling — 90% requests zero export overhead.
    • Batch export: OTLP exporter batches spans — async export thread, minimal request path impact.
    • Agent overhead: Typically <3% CPU — measure in load test before prod.
    • Cardinality explosion: Don't put user_id on metric labels — unbounded series kills Prometheus.
    • Span attributes: Limit high-cardinality attributes on spans — use logs for detail.

    Security considerations

    Observability security:

    • PII in spans: Never attribute email, PAN, password — use opaque IDs only.
    • OTLP TLS: Encrypt export to collector — mTLS in zero-trust environments.
    • Trace data retention: Traces may contain URLs with tokens — scrub query params.
    • RBAC on trace UI: Production traces visible to on-call only — not all engineers.

    Scalability considerations

    Scaling observability pipeline:

    • Collector fan-out: App → regional collector → vendor backend — buffer and batch at scale.
    • Tail sampling at collector: Keep errors and >1s latency — drop happy path bulk.
    • Metrics aggregation: RED per service — don't export raw span as metric per request at scale.
    • Log volume: Structured JSON logs with trace_id — ship to Loki/ELK with retention policy.

    Production challenges

    Real OTel adoption challenges:

    • Broken propagation: Missing traceparent on Feign call — orphan spans; fix with agent or manual interceptor.
    • Async gaps: @Async without context propagation — child work detached trace; use Context.taskWrapping.
    • Dual instrumentation: Old Jaeger + new OTel — duplicate spans; migrate fully, one SDK.
    • Sampling bugs: 0% sampling in prod — no traces; verify OTEL_TRACES_SAMPLER config.
    • Clock skew: Cross-service span timestamps misordered — use NTP on nodes.

    Common mistakes

    • No context propagation across Kafka/async — traces fragment into useless single-service spans.
    • 100% sampling in production — cost explosion and collector overload.
    • High-cardinality metric labels (userId, orderId) — Prometheus OOM.
    • PII in span attributes — compliance violation in trace storage.
    • Ignoring logs correlation — traces without trace_id in logs — half the debugging value lost.

    Debugging guide

    Debug OpenTelemetry in Java services:

    • Verify export: curl http://otel-collector:4318/v1/traces — collector health; check app logs for OTel export errors.
    • Agent debug: OTEL_JAVAAGENT_DEBUG=true — verbose agent startup (dev only).
    • Trace not appearing: Check sampling probability, firewall to collector, wrong endpoint port (4317 gRPC vs 4318 HTTP).
    • Broken parent: Inspect incoming request for traceparent header — missing at gateway = root only.
    bash
    # Enable OTel logging (dev)
    export OTEL_JAVAAGENT_DEBUG=true
    export OTEL_TRACES_EXPORTER=logging # console spans
    # Spring Boot — verify tracing active
    curl -H "traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01" \
    http://localhost:8080/actuator/health
    # Collector pipeline check
    kubectl logs deployment/otel-collector -n observability

    Best practices

    • Adopt W3C tracecontext (traceparent) — standard propagation header.
    • Start with Java agent auto-instrumentation — add manual spans for business operations only.
    • Set OTEL_SERVICE_NAME per Deployment — clear service identity in traces.
    • Use probabilistic sampling in prod (1–10%) + tail sampling for errors.
    • Inject trace_id into structured logs — Micrometer MDC bridge.
    • Propagate context through Kafka headers and @Async executors.
    • Define SLOs from trace metrics — p99 latency, error rate per endpoint.

    Anti-patterns

    • Custom X-Request-Id only — no W3C propagation — incompatible with OTel ecosystem.
    • Manual span for every method — noise; focus on boundaries and business ops.
    • Exporting traces directly to vendor from every pod — bypass collector buffering.
    • Storing full request body in span attributes — size limits and PII risk.
    • Different trace IDs in logs and traces — failing to enable Micrometer-OTel MDC integration.

    Staff engineer notes

    • Staff engineers mandate trace propagation checklist in microservice PR template — HTTP, Kafka, async covered.
    • Observability is not optional at 5+ services — OTel before optimizing performance you can't measure.
    • Sampling is a product decision — 10% sample with error tail capture balances cost and debuggability.
    • Metrics for alerting, traces for debugging, logs for detail — three pillars, one trace_id thread.
    • OpenTelemetry Collector is the control point — switch Tempo to Jaeger without touching Java apps.

    Interview questions

    Interview preparation

    15 questions grouped by difficulty — expand any card for a model answer and follow-up probe.

    Beginner

    5
    1. 1What is OpenTelemetry?
      Beginner

      Model answer

      • Vendor-neutral observability framework for traces, metrics, and logs. Unified SDK, OTLP export protocol, W3C context propagation. CNCF project
      • successor to OpenTracing/OpenCensus merge.

      Follow-up probe

      vs proprietary agents?

    2. 2What is distributed tracing?
      Beginner

      Model answer

      • Track request flow across multiple services as tree of spans sharing trace_id. Root span at entry (gateway), child spans for each downstream call. Waterfall shows latency breakdown
      • find slow service.

      Follow-up probe

      Span vs trace?

    3. 3What is a span?
      Beginner

      Model answer

      Single unit of work with operation name, start/end timestamp, attributes (key-value metadata), optional events.

      Spans have parent reference forming tree.

      Example: 'HTTP GET /orders' or 'JDBC SELECT'.

      Follow-up probe

      Span attributes?

    4. 4What is trace context propagation?
      Beginner

      Model answer

      Pass trace_id and span_id across service boundaries via W3C traceparent HTTP header or message headers.

      Downstream service extracts context, creates child span linked to parent.

      Broken propagation orphans spans.

      Follow-up probe

      traceparent format?

    5. 5Traces vs metrics vs logs?
      Beginner

      Model answer

      Traces: request path latency breakdown (debugging).

      Metrics: aggregated counters/histograms over time (alerting).

      Logs: discrete events with detail (audit).

      OTel correlates via shared trace_id in logs.

      Follow-up probe

      RED method?

    Intermediate

    5
    1. 6How instrument Spring Boot with OTel?
      Intermediate

      Model answer

      Option 1: OpenTelemetry Java agent (-javaagent) auto-instruments Spring MVC, JDBC, Kafka.

      Option 2: Micrometer tracing bridge-otel with Spring Boot 3 actuator.

      Export OTLP to collector.

      probability.

      Follow-up probe

      Agent vs SDK?

    2. 7What is OTLP?
      Intermediate

      Model answer

      • OpenTelemetry Protocol
      • standard export format for traces, metrics, logs. gRPC (4317) or HTTP (4318) to OpenTelemetry Collector or vendor backend. One protocol replaces Jaeger thrift, Prometheus push, etc.

      Follow-up probe

      Collector role?

    3. 8Explain sampling strategies.
      Intermediate

      Model answer

      Head-based: decide at trace start (probabilistic 10%).

      Tail-based: decide after complete (keep errors/slow).

      ParentBased: respect upstream sampling decision.

      Prod uses low head sampling + tail sampling at collector for errors.

      Follow-up probe

      100% sampling when?

    4. 9How propagate trace through Kafka?
      Intermediate

      Model answer

      Inject traceparent into ProducerRecord headers on send using TextMapPropagator.

      Consumer extracts context, starts consumer span as child.

      OTel Java agent auto-instruments Kafka client when enabled.

      Follow-up probe

      Async @Scheduled?

    5. 10What is OpenTelemetry Collector?
      Intermediate

      Model answer

      Vendor-agnostic pipeline: receive OTLP from apps, process (batch, filter, sample), export to Tempo/Jaeger/Prometheus/Datadog.

      Decouples instrumentation from backend.

      Scale collector independently.

      Follow-up probe

      Processor examples?

    Advanced

    5
    1. 11Design observability for 20 Java microservices.
      Advanced

      Model answer

      OTel Java agent all services, OTEL_SERVICE_NAME per deployment, OTLP to regional collector, tail sample errors, Grafana Tempo traces + Prometheus RED metrics, Loki logs with trace_id, W3C propagation HTTP+Kafka, 10% head sampling, SLO dashboard p99 checkout < 500ms, alert on error rate.

      Follow-up probe

      Cost control?

    2. 12Debug 5s latency — tracing approach?
      Advanced

      Model answer

      • Find trace for slow request in Tempo/Jaeger. Waterfall shows span breakdown
      • e.g. payment HTTP 4.8s of 5s. Drill payment span attributes. Check JDBC child spans for slow query. Correlate trace_id logs in payment service at that timestamp.

      Follow-up probe

      No trace found?

    3. 13OTel vs Spring Cloud Sleuth?
      Advanced

      Model answer

      • Sleuth deprecated
      • Micrometer Tracing + OTel bridge is Spring Boot 3 path. OTel vendor-neutral, broader instrumentation, OTLP standard. Migrate Sleuth apps to micrometer-tracing-bridge-otel.

      Follow-up probe

      Brave vs OTel?

    4. 14Cardinality in metrics — pitfalls?
      Advanced

      Model answer

      • Label values create time series
      • userId label with millions values explodes Prometheus memory. Limit labels to service, endpoint, status. High-cardinality data in traces/logs with sampling, not metric labels.

      Follow-up probe

      Exemplars?

    5. 15Context propagation in virtual threads?
      Advanced

      Model answer

      • OpenTelemetry Context attaches to thread
      • virtual threads must propagate Context when task moves carriers. Java agent handles most cases. Manual: Context.current().wrap(runnable) for executor tasks. Test async paths explicitly.

      Follow-up probe

      ThreadLocal issue?

    Hands-on exercise

    Lab: OpenTelemetry concepts:

    • Run playground — review trace hierarchy simulation.
    • Write traceparent header format (version-traceId-spanId-flags).
    • List 3 span attributes for payment charge operation.
    • Explain head vs tail sampling trade-off for prod.
    • Bonus: sketch OTLP flow app → collector → Tempo.

    JavaOpenTelemetry for Java

    Starter Templates
    OutputRemote JVM (Piston · Java 15)
    Press Run to execute. Real javac + JVM via remote API. Falls back to simulator on network failure.

    Architecture trade-offs

    • Java agent vs manual SDK: Agent wins speed; SDK wins control and smaller footprint.
    • Head vs tail sampling: Head wins simplicity; tail wins keep-all-errors without 100% cost.
    • OTLP to collector vs direct vendor: Collector wins flexibility; direct wins simplicity for small setups.
    • 100% traces vs 10% sample: Full wins debuggability; sample wins cost at scale.

    Summary

    OpenTelemetry is the observability foundation for Java microservices — traces show where latency lives, metrics fire alerts, logs provide detail, all linked by trace_id. Deploy the Java agent, fix propagation across Kafka and async, and sample intelligently before production traffic grows. You now have the platform stack: microservices, Kafka, Redis, Docker, Kubernetes, and OTel.

    Key takeaways

    • OpenTelemetry — unified traces, metrics, logs with OTLP export.
    • Distributed tracing — W3C traceparent propagation across HTTP and Kafka.
    • Java agent — fastest Spring Boot instrumentation path.
    • Sampling — probabilistic in prod; tail sample errors at collector.
    • Correlate logs with trace_id — complete the debugging story.
    Ready to mark this lesson complete?Track your journey across the entire course.