# 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. - Published: 2026-09-19 - Updated: 2026-09-19 - Author: Anthony Fillion-Maillet - Tags: spring-boot, observability, opentelemetry, distributed-tracing, micrometer - Reading time: 12 min --- 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. ```java // ObservabilityConfig.java @Configuration public class ObservabilityConfig { @Bean public ObservationRegistryCustomizer 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 org.springframework.boot spring-boot-starter-actuator io.micrometer micrometer-tracing-bridge-otel io.opentelemetry opentelemetry-exporter-otlp ``` 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](https://grafana.com/oss/tempo/) dan [Jaeger](https://www.jaegertracing.io/) 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). ```java // PaymentService.java @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. ## 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`: ```java // AsyncConfig.java @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 org.springframework.boot spring-boot-starter-aop ``` ```yaml # application.yml management: observations: annotations: enabled: true ``` ```java // InventoryService.java @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: | Komponen | Nama Observation | Yang Ditangkap | |----------|-----------------|----------------| | Spring MVC | `http.server.requests` | Request path, method, status, exception | | WebClient | `http.client.requests` | Outbound HTTP call | | RestClient | `http.client.requests` | Outbound HTTP call (Spring 6.1+) | | Spring Kafka | `spring.kafka.listener` | Consumer group, topic, partition | | Spring Data JPA | `spring.data.repository` | Repository method, query time | | Scheduled Tasks | `spring.scheduling` | Task name, execution time | [Dokumentasi Spring Boot Actuator](https://docs.spring.io/spring-boot/reference/actuator/observability.html) mencantumkan semua komponen yang diinstrumentasi otomatis. Library pihak ketiga seperti [Datasource Micrometer](https://github.com/jdbc-observations/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: ```java // ObservationFilterConfig.java @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 %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - traceId=%X{traceId} spanId=%X{spanId} - %msg%n ``` 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](https://opentelemetry.io/docs/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. ## 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. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/id/blog/spring-boot/spring-boot-observability-opentelemetry-distributed-tracing