Observability Spring Boot 2026: OpenTelemetry, Distributed Tracing, dan Pertanyaan Interview

Pelajari observability Spring Boot dengan OpenTelemetry dan Micrometer Tracing. Panduan lengkap konfigurasi distributed tracing, Observation API, ekspor OTLP, serta persiapan interview teknis.

Observability Spring Boot dengan distributed tracing OpenTelemetry

Observability pada Spring Boot menggabungkan logging, metrik, dan distributed tracing menjadi sistem terpadu yang menunjukkan bagaimana request mengalir melalui microservices. Sejak Spring Boot 3, framework ini mengadopsi Micrometer Tracing (menggantikan Spring Cloud Sleuth) dan memperkenalkan Observation API yang menyediakan satu titik instrumentasi untuk menghasilkan metrik dan trace sekaligus.

Pola Observation API

Spring Boot merekomendasikan penggunaan Observation.observe() daripada memanggil OpenTelemetry secara langsung. Satu panggilan instrumentasi menghasilkan metrik via Micrometer dan trace via bridge OpenTelemetry, mengurangi duplikasi kode dan memastikan penamaan tag yang konsisten di semua sinyal.

Bagaimana Observation API Menjembatani Micrometer dan OpenTelemetry

Observation API berperan sebagai facade untuk metrik dan tracing. Ketika kode memanggil Observation.createNotStarted(), Spring Boot mengarahkan observation ke handler yang terdaftar: MeterObservationHandler untuk metrik Micrometer dan TracingObservationHandler untuk distributed trace. Arsitektur ini berarti satu instrumentasi dapat diekspor ke mana saja.

Dependency bridge micrometer-tracing-bridge-otel menghubungkan Micrometer Tracing ke SDK OpenTelemetry. Trace mengalir melalui SdkTracerProvider OpenTelemetry dan diekspor via OTLP ke backend seperti Jaeger, Tempo, atau collector yang kompatibel dengan OpenTelemetry.

ObservabilityConfig.javajava
@Configuration
public class ObservabilityConfig {

    @Bean
    public ObservationRegistryCustomizer<ObservationRegistry> addLowCardinalityTags() {
        return registry -> registry.observationConfig()
            .observationHandler(new ObservationTextPublisher()); // Logs observations to console
    }
}

Konfigurasi di atas mendaftarkan ObservationHandler yang mencatat setiap observation ke log. Di produksi, handler yang dikonfigurasi secara otomatis mengirim data ke registry Micrometer dan exporter OpenTelemetry tanpa kode tambahan.

Dependency yang Diperlukan untuk Tracing Spring Boot 3.4

Spring Boot 3.4 memerlukan dependency eksplisit untuk distributed tracing. spring-boot-starter-actuator menyediakan Observation API, tetapi tracing membutuhkan bridge OpenTelemetry dan exporter.

xml
<!-- pom.xml -->
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
        <groupId>io.micrometer</groupId>
        <artifactId>micrometer-tracing-bridge-otel</artifactId>
    </dependency>
    <dependency>
        <groupId>io.opentelemetry</groupId>
        <artifactId>opentelemetry-exporter-otlp</artifactId>
    </dependency>
</dependencies>

Artifact micrometer-tracing-bridge-otel menjembatani Micrometer Tracing ke API OpenTelemetry. opentelemetry-exporter-otlp mengirim span ke collector menggunakan protokol OTLP melalui HTTP atau gRPC. Spring Boot mengelola penyelarasan versi melalui dependency management.

Mengkonfigurasi Ekspor OTLP ke Jaeger atau Tempo

Spring Boot mengkonfigurasi OtlpHttpSpanExporter secara otomatis ketika dependency OTLP tersedia. Exporter mengirim trace ke endpoint yang ditentukan dalam application.yml.

yaml
# application.yml
management:
  tracing:
    sampling:
      probability: 1.0  # 100% sampling untuk dev, kurangi di produksi
  otlp:
    tracing:
      endpoint: http://localhost:4318/v1/traces
  opentelemetry:
    resource-attributes:
      service.name: order-service
      deployment.environment: staging

resource-attributes melampirkan metadata ke setiap span. Backend seperti Grafana Tempo dan Jaeger menggunakan atribut ini untuk mengelompokkan trace berdasarkan service dan environment. Menetapkan sampling.probability ke 1.0 menangkap semua request selama pengembangan, tetapi sistem produksi biasanya melakukan sampling antara 1% hingga 10% untuk mengontrol biaya penyimpanan.

Membuat Custom Observation dengan Tag Low dan High Cardinality

Observation mendukung dua jenis tag: low cardinality untuk metrik (nilai terbatas seperti HTTP method atau status code) dan high cardinality untuk trace (nilai tidak terbatas seperti user ID atau request ID).

PaymentService.javajava
@Service
public class PaymentService {

    private final ObservationRegistry observationRegistry;

    public PaymentService(ObservationRegistry observationRegistry) {
        this.observationRegistry = observationRegistry;
    }

    public PaymentResult processPayment(PaymentRequest request) {
        return Observation.createNotStarted("payment.process", observationRegistry)
            .lowCardinalityKeyValue("payment.method", request.getMethod().name())  // enum: CARD, BANK_TRANSFER
            .lowCardinalityKeyValue("currency", request.getCurrency())              // bounded: USD, EUR, GBP
            .highCardinalityKeyValue("payment.id", request.getPaymentId())          // unique per request
            .highCardinalityKeyValue("customer.id", request.getCustomerId())        // high cardinality
            .observe(() -> executePayment(request));
    }

    private PaymentResult executePayment(PaymentRequest request) {
        // Payment gateway call
        return new PaymentResult(true, "TXN-" + UUID.randomUUID());
    }
}

Tag low cardinality muncul di metrik dan trace. Tag high cardinality hanya muncul di trace karena metrik dengan dimensi tidak terbatas akan membuat penyimpanan membengkak. Pembedaan ini mencegah cardinality bomb pada Prometheus sambil menjaga detail trace tetap kaya.

Pertanyaan Interview: Tag Cardinality

Pewawancara sering bertanya mengapa beberapa tag harus dikecualikan dari metrik. Jawabannya melibatkan cardinality: tag user ID pada metrik membuat satu time series per user, berpotensi jutaan series. Backend monitoring kesulitan dengan high cardinality, menyebabkan kehabisan memori dan query lambat. Trace menangani high cardinality melalui sampling, menjadikannya tempat yang tepat untuk identifier spesifik request.

Siap menguasai wawancara Spring Boot Anda?

Berlatih dengan simulator interaktif, flashcards, dan tes teknis kami.

Propagasi Trace Context Melintasi Async Boundary

Distributed tracing memerlukan propagasi context. Header HTTP membawa trace ID antar service, tetapi operasi async seperti method @Async atau chain CompletableFuture dapat kehilangan context jika tidak dikonfigurasi.

Spring Boot 3.4 menyediakan propagasi otomatis untuk reactive stream dan method @Async ketika dikonfigurasi:

yaml
# application.yml
spring:
  reactor:
    context-propagation: auto
  task:
    execution:
      propagate-context: true

Untuk bean TaskExecutor custom, bungkus dengan ContextPropagatingTaskDecorator:

AsyncConfig.javajava
@Configuration
@EnableAsync
public class AsyncConfig {

    @Bean
    public TaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10);
        executor.setMaxPoolSize(50);
        executor.setTaskDecorator(new ContextPropagatingTaskDecorator());
        executor.initialize();
        return executor;
    }
}

Decorator menyalin Observation saat ini dan trace context ke thread async. Tanpa itu, span yang dimulai di task async tidak memiliki parent, memutus pohon trace.

Menggunakan Anotasi @Observed dan @NewSpan

Spring Boot 3.4 mendukung observability deklaratif melalui anotasi. Aktifkan pemrosesan anotasi dengan menambahkan AspectJ weaver:

xml
<!-- pom.xml -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
yaml
# application.yml
management:
  observations:
    annotations:
      enabled: true
InventoryService.javajava
@Service
public class InventoryService {

    @Observed(name = "inventory.check", contextualName = "checkStock")
    public StockLevel checkStock(String productId) {
        // Database query
        return new StockLevel(productId, 42);
    }

    @NewSpan("inventory.reserve")  // Creates a child span under the current trace
    public ReservationResult reserveStock(String productId, int quantity) {
        // Update inventory
        return new ReservationResult(true, "RES-" + System.currentTimeMillis());
    }
}

@Observed membuat metrik (timer) dan span sekaligus. @NewSpan hanya membuat span, berguna ketika operasi sudah memiliki metrik di tempat lain. Kedua anotasi secara otomatis menangani pencatatan exception dan status span.

Komponen yang Diinstrumentasi Otomatis di Spring Boot 3.4

Spring Boot 3.4 menginstrumentasi beberapa komponen secara otomatis tanpa perubahan kode:

KomponenNama ObservationYang Ditangkap
Spring MVChttp.server.requestsRequest path, method, status, exception
WebClienthttp.client.requestsOutbound HTTP call
RestClienthttp.client.requestsOutbound HTTP call (Spring 6.1+)
Spring Kafkaspring.kafka.listenerConsumer group, topic, partition
Spring Data JPAspring.data.repositoryRepository method, query time
Scheduled Tasksspring.schedulingTask name, execution time

Dokumentasi Spring Boot Actuator mencantumkan semua komponen yang diinstrumentasi otomatis. Library pihak ketiga seperti Datasource Micrometer menambahkan tracing query JDBC.

Memfilter Observation untuk Mengurangi Noise

Health check, readiness probe, dan resource statis menghasilkan noise di trace. Filter menggunakan predicate:

ObservationFilterConfig.javajava
@Configuration
public class ObservationFilterConfig {

    @Bean
    public ObservationPredicate noHealthChecks() {
        return (name, context) -> !name.equals("http.server.requests")
            || !isHealthEndpoint(context);
    }

    private boolean isHealthEndpoint(Observation.Context context) {
        if (context instanceof ServerRequestObservationContext http) {
            String path = http.getCarrier().getRequestURI();
            return path.startsWith("/actuator/health") || path.startsWith("/actuator/ready");
        }
        return false;
    }
}

Alternatifnya, nonaktifkan observation berdasarkan nama di konfigurasi:

yaml
# application.yml
management:
  observations:
    enable:
      spring.security: false  # Disable Spring Security observations
      http.server.requests.actuator: false  # Custom predicate name
Konteks Interview: Trace Sampling vs. Filtering

Sampling mengurangi persentase trace yang dikumpulkan di semua endpoint. Filtering menghapus endpoint tertentu sepenuhnya. Gunakan filtering untuk endpoint yang tidak pernah memberikan nilai diagnostik (health check, metrics scraping). Gunakan sampling untuk mengontrol biaya sambil mempertahankan data representatif.

Mengkorelasikan Log dengan Trace ID

Spring Boot 3.4 secara otomatis menambahkan trace dan span ID ke MDC (Mapped Diagnostic Context) ketika Micrometer Tracing aktif. Logback dan Log4j2 dapat menyertakan ID ini dalam output log:

xml
<!-- logback-spring.xml -->
<configuration>
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - traceId=%X{traceId} spanId=%X{spanId} - %msg%n</pattern>
        </encoder>
    </appender>
    <root level="INFO">
        <appender-ref ref="CONSOLE" />
    </root>
</configuration>

Dengan pola ini, setiap baris log menyertakan trace ID. Pencarian log berdasarkan trace ID mengembalikan semua pesan dari satu request di semua service, mengkorelasikan log dengan span di Jaeger atau Tempo.

Pertanyaan Interview Umum tentang Observability Spring Boot

Pewawancara menilai pemahaman konseptual dan pengalaman praktis dengan observability. Pertanyaan-pertanyaan ini sering muncul di posisi backend senior.

T: Apa perbedaan antara Micrometer Tracing dan OpenTelemetry?

Micrometer Tracing adalah API vendor-neutral untuk distributed tracing, mirip dengan bagaimana Micrometer mengabstraksi metrik. OpenTelemetry adalah implementasi dan protokol spesifik. Spring Boot menggunakan Micrometer Tracing sebagai API dan menjembatani ke OpenTelemetry untuk ekspor via micrometer-tracing-bridge-otel. Layering ini memungkinkan aplikasi berpindah backend tanpa perubahan kode.

T: Mengapa Spring merekomendasikan Observation API daripada instrumentasi OpenTelemetry langsung?

Observation API menyediakan satu titik instrumentasi yang menghasilkan metrik dan trace. Panggilan OpenTelemetry langsung hanya menghasilkan trace. Menggunakan Observation.observe() menghasilkan timer metric dan span dari kode yang sama, mengurangi duplikasi dan memastikan penamaan konsisten.

T: Bagaimana cara debug trace yang menunjukkan gap antar span?

Gap menunjukkan instrumentasi yang hilang atau propagasi context yang rusak. Periksa apakah kode menggunakan operasi async tanpa ContextPropagatingTaskDecorator. Verifikasi bahwa HTTP client diinstrumentasi (WebClient, RestClient, atau RestTemplate yang dibungkus manual). Untuk message queue, konfirmasi bahwa header trace menyebar melalui properti pesan.

T: Apa yang terjadi jika menambahkan tag high cardinality ke metrik?

Setiap nilai tag unik membuat time series baru. Tag user ID dengan jutaan user membuat jutaan series, menghabiskan memori di Prometheus atau backend TSDB lainnya. Solusinya adalah menggunakan highCardinalityKeyValue() alih-alih lowCardinalityKeyValue(), yang menambahkan tag hanya ke trace di mana high cardinality diharapkan.

Membangun Pipeline Distributed Tracing dengan Docker Compose

Stack observability lokal membantu memvalidasi instrumentasi sebelum deploy ke produksi. Contoh ini menggunakan OpenTelemetry Collector dan Jaeger:

yaml
# docker-compose.yml
services:
  otel-collector:
    image: otel/opentelemetry-collector-contrib:0.102.0
    command: ["--config", "/etc/otel-collector-config.yml"]
    volumes:
      - ./otel-collector-config.yml:/etc/otel-collector-config.yml
    ports:
      - "4318:4318"   # OTLP HTTP
      - "4317:4317"   # OTLP gRPC

  jaeger:
    image: jaegertracing/jaeger:2.3
    ports:
      - "16686:16686"  # UI
    environment:
      - COLLECTOR_OTLP_ENABLED=true

  app:
    build: .
    environment:
      - MANAGEMENT_OTLP_TRACING_ENDPOINT=http://otel-collector:4318/v1/traces
    depends_on:
      - otel-collector
yaml
# otel-collector-config.yml
receivers:
  otlp:
    protocols:
      http:
        endpoint: 0.0.0.0:4318
      grpc:
        endpoint: 0.0.0.0:4317

processors:
  batch:
    timeout: 1s
    send_batch_size: 1024

exporters:
  otlp/jaeger:
    endpoint: jaeger:4317
    tls:
      insecure: true

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch]
      exporters: [otlp/jaeger]

Collector menerima span dari aplikasi Spring Boot dan meneruskannya ke Jaeger. Arsitektur ini memungkinkan penambahan exporter (Tempo, Zipkin, cloud backend) tanpa mengubah kode aplikasi.

Mulai berlatih!

Uji pengetahuan Anda dengan simulator wawancara dan tes teknis kami.

Poin Penting untuk Observability Spring Boot

  • Observation API menyatukan metrik dan tracing: satu panggilan Observation.observe() menghasilkan timer metric dan span, menghilangkan instrumentasi ganda.
  • Tambahkan micrometer-tracing-bridge-otel dan opentelemetry-exporter-otlp untuk mengaktifkan ekspor OTLP. Spring Boot 3.4 mengkonfigurasi HTTP exporter secara otomatis dengan endpoint dari management.otlp.tracing.endpoint.
  • Gunakan lowCardinalityKeyValue() untuk dimensi yang muncul di metrik (nilai terbatas). Gunakan highCardinalityKeyValue() untuk data trace-only (user ID, request ID).
  • Aktifkan propagasi context untuk kode async dengan spring.task.execution.propagate-context=true dan bungkus executor custom dengan ContextPropagatingTaskDecorator.
  • Filter endpoint yang berisik seperti /actuator/health menggunakan bean ObservationPredicate untuk menjaga trace tetap fokus pada operasi bisnis.
  • Pola log harus menyertakan %X{traceId} untuk mengkorelasikan baris log dengan distributed trace di backend observability.
  • Pertanyaan interview tentang observability fokus pada cardinality, kegagalan propagasi context, dan perbedaan antara Observation API dan penggunaan OpenTelemetry langsung.
Tantangan harian

Bisakah kamu menemukan bug di Spring Boot?

Satu potongan kode nyata, satu bug tersembunyi, satu percobaan per hari. Tanpa akun untuk mencoba.

Anthony Fillion-Maillet

Ditulis oleh

Anthony Fillion-Maillet

Pendiri SharpSkill

Developer fullstack selama lebih dari 10 tahun. Ia menjalankan SharpSkill dan bertanggung jawab atas semua yang diterbitkan di sini.

Diperbarui 19 September 2026

Tag

#spring-boot
#observability
#opentelemetry
#distributed-tracing
#micrometer

Bagikan

Artikel terkait