TungDaDev's Blog

distributed tracing với micrometer & opentelemetry trong spring boot 3

Distributed tracing micrometer.jpg
Published on
/7 mins read/

Trong kiến trúc nguyên khối (Monolith), debug lỗi chỉ cần nhìn vào Stack Trace. Trong kiến trúc Microservices, nếu không có Distributed Tracing, việc tìm kiếm nguồn gốc một lỗi 500 chẳng khác nào mò kim đáy bể giữa hàng triệu dòng log rời rạc.

Hãy tưởng tượng một ngày đẹp trời, hệ thống của bạn nhận được khiếu nại từ khách hàng: "Tôi bấm thanh toán đơn hàng nhưng giao diện cứ quay tròn suốt 30 giây rồi báo lỗi timeout!".

Hành trình của một cú click đó đi qua:

  1. API Gateway
  2. Auth Service (Xác thực JWT)
  3. Order Service (Tạo đơn hàng)
  4. Bắn message qua Kafka
  5. Payment Service (Gọi cổng thanh toán bên thứ ba)
  6. Notification Service (Gửi email/SMS)

Bạn mở log của cả 6 service lên: Mỗi service ghi nhận hàng ngàn dòng log mỗi giây. Làm sao bạn biết dòng log nào ở Payment Service thuộc về chính xác request bị lỗi của người dùng đó? Và khâu nào trong chuỗi 6 bước trên là thủ phạm gây nghẽn 30 giây?

Câu trả lời nằm ở Distributed Tracing (Lần vết phân tán).

Trong Spring Boot 3, công cụ kinh điển Spring Cloud Sleuth đã chính thức ngừng phát triển và được thay thế bằng Micrometer Tracing kết hợp chuẩn mở toàn cầu OpenTelemetry (OTel).

Bài viết này sẽ hướng dẫn bạn thiết lập một hệ sinh thái Observability chuẩn chỉnh từ con số không.


# các khái niệm nguyên tử trong distributed tracing

Để làm chủ Tracing, bạn chỉ cần nắm vững 3 viên gạch nền tảng:

  1. Trace (Hành trình toàn vẹn): Đại diện cho toàn bộ chuyến đi của một request từ lúc người dùng gửi đi cho đến khi nhận về kết quả. Toàn bộ hành trình này được gắn một mã định danh duy nhất gọi là Trace ID.
  2. Span (Một chặng dừng chân): Là một đơn vị công việc riêng lẻ bên trong hành trình. Ví dụ: một cuộc gọi HTTP, một câu lệnh SQL query, hoặc một lần publish message lên Kafka. Mỗi Span có Span ID, thời gian bắt đầu, thời gian kết thúc và Parent Span ID để tạo thành một cây phả hệ (Trace Tree).
  3. Context Propagation (Lan truyền ngữ cảnh): Cơ chế "chuyền tay" Trace ID qua biên giới các máy chủ khác nhau thông qua HTTP Headers (theo chuẩn quốc tế W3C Trace Context: traceparent) hoặc Kafka Record Headers.

# từ spring cloud sleuth sang micrometer tracing

Trong Spring Boot 2, chúng ta dùng spring-cloud-starter-sleuth. Tuy nhiên, Sleuth bị ràng buộc quá chặt vào hệ sinh thái của Spring Cloud và thư viện Brave/Zipkin cũ kỹ.

Trong Spring Boot 3:

  • Micrometer Tracing đóng vai trò là một Lớp trừu tượng (Facade) — tương tự như cách SLF4J trừu tượng hóa cho Logback hay Log4j2.
  • Nó cho phép bạn chọn lựa "bộ não" bên dưới: Dùng OpenTelemetry SDK (tiêu chuẩn của Cloud Native Computing Foundation - CNCF) hoặc Brave mà không cần thay đổi một dòng code nghiệp vụ nào!

# hướng dẫn cấu hình thực chiến với opentelemetry & grafana tempo

1. Khai báo Dependencies (pom.xml)

Để sử dụng Micrometer Tracing với OpenTelemetry trong Spring Boot 3:

<dependencies>
    <!-- Spring Boot Actuator: Nền tảng quan sát -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
 
    <!-- Cầu nối Micrometer Tracing với OpenTelemetry -->
    <dependency>
        <groupId>io.micrometer</groupId>
        <artifactId>micrometer-tracing-bridge-otel</artifactId>
    </dependency>
 
    <!-- Exporter: Đẩy Spans sang OpenTelemetry Collector / Grafana Tempo qua OTLP gRPC hoặc HTTP -->
    <dependency>
        <groupId>io.opentelemetry</groupId>
        <artifactId>opentelemetry-exporter-otlp</artifactId>
    </dependency>
</dependencies>

2. Cấu hình trong application.yml

management:
  endpoints:
    web:
      exposure:
        include: health, info, metrics, prometheus
  tracing:
    sampling:
      probability: 1.0 # Thu thập 100% trace trong môi trường Dev/Staging (Production nên để 0.1 = 10%)
    propagation:
      type: W3C # Chuẩn W3C Trace Context (traceparent)
  otlp:
    tracing:
      endpoint: 'http://tempo-collector:4318/v1/traces' # Đẩy về Grafana Tempo
      timeout: 2s
 
# Tự động nhét TraceId & SpanId vào format log
logging:
  pattern:
    level: '%5p [${spring.application.name:},%X{traceId:-},%X{spanId:-}]'

# sức mạnh kỳ diệu: log correlation (kết nối log với trace)

Một trong những tính năng giá trị nhất của Micrometer Tracing là Log Correlation: Mỗi khi một request đi vào hệ thống, Micrometer sẽ tự động tiêm traceId và spanId vào MDC (Mapped Diagnostic Context) của SLF4J.

Khi bạn ghi log:

log.info("Bắt đầu xử lý thanh toán cho đơn hàng {}", orderId);

Log in ra terminal hoặc file sẽ tự động có dạng:

2026-09-10 14:20:15.123  INFO [order-service,7a89f012b34c5678,9876e543210fedcb] : Bắt đầu xử lý thanh toán cho đơn hàng ORD-999

Trải nghiệm gỡ lỗi đỉnh cao trên Grafana:

  1. Bạn mở Grafana Loki để xem log của order-service.
  2. Bạn nhìn thấy một dòng log màu đỏ báo lỗi: Connection Timeout to Stripe.
  3. Bên cạnh dòng log có một nút bấm nhỏ màu xanh: Tempo: 7a89f012b34c5678.
  4. Bạn click vào nút đó: Grafana lập tức chia đôi màn hình và vẽ ra toàn bộ biểu đồ Waterfall (thời gian chạy) của cả 6 microservices!
  5. Bạn nhìn thấy ngay lập tức: Bước gọi sang Stripe API ngốn mất 29.8 giây trong tổng số 30 giây! Không cần đoán mò, không cần hỏi đồng nghiệp, nguyên nhân gốc rễ (Root Cause) lộ diện chỉ sau đúng 3 giây!

# truyền ngữ cảnh qua kafka (context propagation over messaging)

Khi service này bắn message sang service khác qua Kafka, làm sao Trace ID không bị đứt đoạn?

Spring for Apache Kafka tự động tích hợp sẵn với Micrometer Observation. Bạn chỉ cần bật cờ:

@Bean
public ConcurrentKafkaListenerContainerFactory<String, String> kafkaListenerContainerFactory(
        ConsumerFactory<String, String> consumerFactory) {
    ConcurrentKafkaListenerContainerFactory<String, String> factory =
            new ConcurrentKafkaListenerContainerFactory<>();
    factory.setConsumerFactory(consumerFactory);
    factory.getContainerProperties().setObservationEnabled(true); // Bật tracing cho Kafka Consumer!
    return factory;
}
 
@Bean
public KafkaTemplate<String, String> kafkaTemplate(ProducerFactory<String, String> producerFactory) {
    KafkaTemplate<String, String> template = new KafkaTemplate<>(producerFactory);
    template.setObservationEnabled(true); // Tự động gắn W3C Headers vào Kafka Producer!
    return template;
}

Bây giờ, Trace ID sẽ đi xuyên qua ranh giới giữa HTTP và Message Queue một cách mượt mà và liền mạch!


# tổng kết

Nếu bạn không thể đo lường và quan sát hệ thống ở quy mô phân tán, bạn không thực sự làm chủ kiến trúc microservices; bạn chỉ đang sở hữu một mớ hỗn độn được chia nhỏ ra mà thôi.

Việc áp dụng Micrometer Tracing và OpenTelemetry trong Spring Boot 3 không đòi hỏi bạn phải thay đổi logic nghiệp vụ, nhưng nó mang lại cho toàn bộ đội ngũ kỹ thuật một "chiếc kính viễn vọng siêu nét" để nhìn thấu mọi ngóc ngách của hệ thống trong môi trường Production.


Chỉ là những ghi chép cá nhân với hy vọng mang lại chút giá trị. Nếu thấy hữu ích, đừng ngại chia sẻ cho bạn bè & đồng nghiệp nhé!

Happy coding 😎 👍🏻 🚀 🔥.