Spring Boot 4 Complete Guide · Lektion

Logs, Metriken und Traces korrelieren

Vereinheitlichen Sie die Telemetrie, indem Sie strukturierte Logs, Metriken und Traces für eine schnelle Störungsdiagnose korrelieren.

Lektion 4 von 413 Schritte

Logs, Metriken und Traces korrelieren ist eine kostenlose Spring Boot 4 Complete Guide-Lektion auf CoddyKit. Dies ist Lektion 4 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des Spring Boot 4 Complete Guide-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der Spring Boot 4 Complete Guide-Kurs umfasst insgesamt 4 Lektionen.

Teile dieser Lektion wurden noch nicht übersetzt und werden auf Englisch angezeigt.

The Three Pillars Problem

Observability rests on three signal types, each answering a different question:

  • Logs — discrete events: what happened at a point in time.
  • Metrics — aggregated numbers: how much / how often over time.
  • Traces — causal request flow: where time was spent across services.

During an incident, looking at each pillar in isolation is slow. You see a latency spike in a metric, but you cannot jump to the exact logs or the slow span. The goal of this lesson is correlation: stitch the three together so one signal links directly to the others, collapsing diagnosis time from minutes to seconds.

The Glue: trace_id and span_id

The key that unifies all three pillars is the trace context propagated by OpenTelemetry / Micrometer Tracing. Two identifiers matter:

  • traceId — a unique id for the entire request as it crosses services.
  • spanId — a unique id for one unit of work inside that trace.

If every log line, every metric exemplar, and every span carries the same traceId, a single id lets you pivot between pillars. In Spring Boot 4, Micrometer Tracing automatically places these ids into the SLF4J MDC (Mapped Diagnostic Context), so your logs inherit them with zero per-line code.

Wiring Up the Dependencies

To get automatic correlation in a Spring Boot 4 service, you need the Actuator, Micrometer Tracing bridge, and an exporter. The bridge connects Spring observations to a tracer; the exporter ships spans to a backend like Tempo, Jaeger, or Zipkin.

A typical Maven setup:

<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
  </dependency>
  <!-- Bridge Micrometer Observation -> OpenTelemetry tracer -->
  <dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-tracing-bridge-otel</artifactId>
  </dependency>
  <!-- Export spans over OTLP to Tempo/Jaeger -->
  <dependency>
    <groupId>io.opentelemetry</groupId>
    <artifactId>opentelemetry-exporter-otlp</artifactId>
  </dependency>
  <!-- Push metrics to Prometheus -->
  <dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-prometheus</artifactId>
  </dependency>
</dependencies>

Logs That Carry the Trace Id

Micrometer Tracing injects traceId and spanId into the MDC. To surface them, reference the MDC keys in your logging pattern. Spring Boot 4 ships a convenient property that prepends the application name plus ids:

  • logging.pattern.correlation controls the correlation segment.
  • The values come from MDC keys traceId and spanId.

Example application.yml:

spring:
  application:
    name: orders-service

management:
  tracing:
    sampling:
      probability: 1.0          # sample everything in dev
  otlp:
    tracing:
      endpoint: http://tempo:4318/v1/traces

logging:
  pattern:
    correlation: "[${spring.application.name},%X{traceId:-},%X{spanId:-}] "
  level:
    org.springframework.web: INFO

What a Correlated Log Line Looks Like

With the pattern in place, every log statement automatically prints the trace context. You write ordinary logging code; the ids appear for free because they live in the MDC:

The resulting output looks like:

2026-06-10 09:14:02.331 INFO [orders-service,3f9a1c0b8e4d77a2,8e4d77a2c1b0] o.e.OrderService : Placing order 4821 for customer 77

The middle bracket holds appName,traceId,spanId. Now any human or log query (Loki, Elasticsearch) can filter by traceId and retrieve every line emitted while serving that one request — across threads and even across services.

@Service
public class OrderService {
    private static final Logger log = LoggerFactory.getLogger(OrderService.class);

    public Order place(long customerId, Cart cart) {
        log.info("Placing order for customer {}", customerId);
        Order order = persist(customerId, cart);
        log.info("Order {} confirmed", order.id());
        return order;
    }
}

Reading the MDC Programmatically

Sometimes you need the trace id in code — to return it in an error response so a user can quote it to support, or to attach it to an outgoing message. The ids live in the SLF4J MDC, a thread-local map. You can read them directly:

This is a complete, standalone illustration of how MDC works — no Spring required. In a real service Micrometer populates these keys for you; here we set them manually to show the mechanism.

import org.slf4j.MDC;

public class MdcDemo {
    public static void main(String[] args) {
        // Micrometer Tracing normally sets these per-request.
        MDC.put("traceId", "3f9a1c0b8e4d77a2");
        MDC.put("spanId", "8e4d77a2c1b0");

        String traceId = MDC.get("traceId");
        String spanId = MDC.get("spanId");
        System.out.println("trace=" + traceId + " span=" + spanId);

        // Echo back to the client so they can quote it to support.
        System.out.println("{\"error\":\"failed\",\"traceId\":\"" + traceId + "\"}");

        MDC.clear();
    }
}

Custom Spans with @Observed

Auto-instrumentation covers controllers, RestClient/WebClient, and JDBC. For your own business operations, create explicit observations. The @Observed annotation (from Micrometer) wraps a method in an observation that becomes both a span and a timer metric — one annotation feeds two pillars.

Enable it with an ObservedAspect bean, then annotate:

@Configuration
public class ObservabilityConfig {
    @Bean
    ObservedAspect observedAspect(ObservationRegistry registry) {
        return new ObservedAspect(registry);
    }
}

@Service
public class PricingService {

    @Observed(name = "pricing.calculate",
              contextualName = "calculate-price",
              lowCardinalityKeyValues = {"tier", "premium"})
    public Money calculate(Cart cart) {
        // span 'calculate-price' + timer 'pricing.calculate' emitted automatically
        return cart.lines().stream()
                   .map(Line::subtotal)
                   .reduce(Money.ZERO, Money::add);
    }
}

Exemplars: Linking Metrics to Traces

Metrics are aggregates, so they lose the individual request. Exemplars bridge this gap: a Prometheus histogram bucket can attach a sample traceId for one of the requests that fell into that bucket.

So when you stare at a p99 latency spike in Grafana, you click the exemplar dot on the histogram and jump straight to the offending trace. Micrometer + the Prometheus registry emit exemplars automatically when a tracer is on the classpath.

  • Metric tells you that something is slow.
  • Exemplar gives you one concrete trace id.
  • Trace shows you where the time went.
  • That trace id filters the logs for the full story.

Propagating Context Across Threads

Trace context is stored in a ThreadLocal. When you hand work to an @Async method or an executor, the context does not follow automatically and your child logs lose the traceId.

The fix in Spring Boot 4: wrap your executor with Micrometer's ContextSnapshot support (or use ContextExecutorService). Spring's TaskDecorator integration propagates both the MDC and the observation scope:

@Configuration
@EnableAsync
public class AsyncConfig {

    @Bean
    TaskDecorator otelTaskDecorator() {
        // Re-attaches the captured trace/MDC context inside the worker thread
        return runnable -> {
            var snapshot = io.micrometer.context.ContextSnapshotFactory
                    .builder().build().captureAll();
            return () -> {
                try (var scope = snapshot.setThreadLocals()) {
                    runnable.run();
                }
            };
        };
    }

    @Bean
    Executor taskExecutor(TaskDecorator decorator) {
        var ex = new ThreadPoolTaskExecutor();
        ex.setTaskDecorator(decorator);
        ex.initialize();
        return ex;
    }
}

Correlation Meets Resilience

Resilience patterns and correlation reinforce each other. When a Resilience4j circuit breaker opens or a retry fires, that event should be visible in all three pillars under the same traceId:

  • The span records the fallback and its error tag.
  • A metric (resilience4j.circuitbreaker.calls) counts the failures.
  • The log line, carrying the trace id, explains which downstream call tripped the breaker.

Annotate the fallback path so the failure stays attached to the originating trace:

@Service
public class InventoryClient {
    private static final Logger log = LoggerFactory.getLogger(InventoryClient.class);

    @CircuitBreaker(name = "inventory", fallbackMethod = "fromCache")
    @Retry(name = "inventory")
    public Stock check(String sku) {
        return restClient.get().uri("/stock/{sku}", sku)
                         .retrieve().body(Stock.class);
    }

    private Stock fromCache(String sku, Throwable cause) {
        // Same traceId as the failed call -> diagnosable end to end
        log.warn("inventory call failed for {}, serving cached stock", sku, cause);
        return cache.getOrEmpty(sku);
    }
}

The Diagnosis Workflow

Put it together as a repeatable incident drill. A pager fires on an SLO burn-rate alert built from metrics. From there:

  • Step 1 — Metric: Grafana dashboard shows p99 on http.server.requests spiking for POST /orders.
  • Step 2 — Exemplar: Click the exemplar on the histogram; it carries a traceId.
  • Step 3 — Trace: Open that trace in Tempo; the waterfall shows a 4-second span on the inventory call.
  • Step 4 — Logs: Pivot from the trace to Loki filtered by the same traceId; the warn log reveals a circuit breaker opened after timeouts.

Four clicks, one id, root cause found. That is the payoff of correlation.

Quick Check

Test your understanding of the correlation mechanism.

Recap

You unified the three observability pillars around a single shared key:

  • traceId/spanId propagated by Micrometer Tracing are the glue across logs, metrics, and traces.
  • Logs inherit the ids automatically from the SLF4J MDC via logging.pattern.correlation — no per-line code.
  • @Observed turns one business method into both a span and a timer, feeding traces and metrics at once.
  • Exemplars let a metric spike point to one concrete trace, and that trace id then filters the logs.
  • Context propagation (TaskDecorator / ContextSnapshot) keeps the trace alive across async threads.
  • Resilience events (circuit breaker, retry fallback) stay attached to the originating trace, so failures are diagnosable end to end.

The result is a four-click incident workflow: metric → exemplar → trace → logs, all bound by one id.

Kostenlos starten

Lerne Java mit einem KI-Tutor — kostenlos

Schreibe und führe echten Code in deinem Browser aus, bekomme sofortige Hilfe von einem 24/7 KI-Tutor und setze dein Lernen im Web oder in der App fort.

Kurse
21
Lektionen
84

Häufig gestellte Fragen

Ist die Lektion „Logs, Metriken und Traces korrelieren“ kostenlos?

Ja — der vollständige Text von „Logs, Metriken und Traces korrelieren“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des Spring Boot 4 Complete Guide-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der Spring Boot 4 Complete Guide-Kurs umfasst insgesamt 4 Lektionen.

Was lerne ich in „Logs, Metriken und Traces korrelieren“?

Vereinheitlichen Sie die Telemetrie, indem Sie strukturierte Logs, Metriken und Traces für eine schnelle Störungsdiagnose korrelieren. Du übst Spring Boot 4 Complete Guide mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.

Brauche ich Erfahrung, um Spring Boot 4 Complete Guide zu starten?

Keine Vorkenntnisse erforderlich. Spring Boot 4 Complete Guide auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 4 von 4.

Wie lange dauert die Lektion „Logs, Metriken und Traces korrelieren“?

Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.

Kann ich in dieser Spring Boot 4 Complete Guide-Lektion Code schreiben und ausführen?

Ja. Jede Spring Boot 4 Complete Guide-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.

Alle Lektionen in diesem Kurs

  1. Kontextweitergabe und Span-Instrumentierung
  2. Circuit Breaker und Bulkhead-Isolierung
  3. Ratenbegrenzung, Wiederholungen und Zeitbegrenzer
  4. Logs, Metriken und Traces korrelieren
← Zurück zu Spring Boot 4 Complete Guide