# 2026'da Spring Boot Gözlemlenebilirlik: OpenTelemetry, Dağıtık İzleme ve Mülakat Soruları > OpenTelemetry ve Micrometer Tracing ile Spring Boot gözlemlenebilirliği hakkında kapsamlı rehber. Dağıtık izleme kurulumu, Observation API, OTLP export yapılandırması ve teknik mülakat hazırlığı. - Published: 2026-09-19 - Updated: 2026-09-19 - Author: Anthony Fillion-Maillet - Tags: spring-boot, observability, opentelemetry, distributed-tracing, micrometer - Reading time: 12 min --- Spring Boot gözlemlenebilirliği, loglama, metrikler ve dağıtık izlemeyi birleştirerek isteklerin mikro servisler arasındaki akışını gösteren birleşik bir sistem oluşturur. Spring Boot 3 ile birlikte framework, Micrometer Tracing'i benimsedi (Spring Cloud Sleuth'un yerini aldı) ve hem metrikler hem de trace'ler üreten tek bir enstrümantasyon noktası sağlayan Observation API'yi tanıttı. > **Observation API Deseni** > > Spring Boot, OpenTelemetry'yi doğrudan çağırmak yerine `Observation.observe()` kullanılmasını önerir. Tek bir enstrümantasyon çağrısı, Micrometer aracılığıyla metrikler ve OpenTelemetry köprüsü aracılığıyla trace'ler üretir; bu sayede kod tekrarı azalır ve sinyaller arasında tutarlı etiket adlandırması sağlanır. ## Observation API Micrometer ve OpenTelemetry'yi Nasıl Birleştirir Observation API, hem metrikler hem de izleme üzerinde bir facade görevi görür. Kod `Observation.createNotStarted()` çağırdığında, Spring Boot gözlemi kayıtlı handler'lara yönlendirir: Micrometer metrikleri için `MeterObservationHandler` ve dağıtık trace'ler için `TracingObservationHandler`. Bu mimari, bir kez yapılan enstrümantasyonun her yere export edilmesi anlamına gelir. Köprü bağımlılığı `micrometer-tracing-bridge-otel`, Micrometer Tracing'i OpenTelemetry'nin SDK'sına bağlar. Trace'ler OpenTelemetry'nin `SdkTracerProvider`'ı üzerinden akar ve OTLP aracılığıyla Jaeger, Tempo veya herhangi bir OpenTelemetry uyumlu toplayıcıya export edilir. ```java // ObservabilityConfig.java @Configuration public class ObservabilityConfig { @Bean public ObservationRegistryCustomizer addLowCardinalityTags() { return registry -> registry.observationConfig() .observationHandler(new ObservationTextPublisher()); // Gözlemleri konsola loglar } } ``` Yukarıdaki yapılandırma, her gözlemi loglayan bir `ObservationHandler` kaydeder. Üretim ortamında, otomatik yapılandırılmış handler'lar ek kod olmadan verileri Micrometer kayıtlarına ve OpenTelemetry exporter'larına gönderir. ## Spring Boot 3.4 Tracing için Gerekli Bağımlılıklar Spring Boot 3.4, dağıtık izleme için açık bağımlılıklar gerektirir. `spring-boot-starter-actuator` Observation API'yi sağlar, ancak izleme OpenTelemetry köprüsü ve bir exporter gerektirir. ```xml org.springframework.boot spring-boot-starter-actuator io.micrometer micrometer-tracing-bridge-otel io.opentelemetry opentelemetry-exporter-otlp ``` `micrometer-tracing-bridge-otel` artifakt'ı, Micrometer Tracing'i OpenTelemetry'nin API'sine köprüler. `opentelemetry-exporter-otlp`, span'leri HTTP veya gRPC üzerinden OTLP protokolü kullanarak toplayıcılara gönderir. Spring Boot, bağımlılık yönetimi aracılığıyla sürüm hizalamasını yönetir. ## Jaeger veya Tempo'ya OTLP Export Yapılandırması Spring Boot, OTLP bağımlılığı mevcut olduğunda `OtlpHttpSpanExporter`'ı otomatik yapılandırır. Exporter, `application.yml`'de belirtilen endpoint'e trace'leri gönderir. ```yaml # application.yml management: tracing: sampling: probability: 1.0 # Dev için %100 örnekleme, üretimde azaltın otlp: tracing: endpoint: http://localhost:4318/v1/traces opentelemetry: resource-attributes: service.name: order-service deployment.environment: staging ``` `resource-attributes`, her span'e metadata ekler. [Grafana Tempo](https://grafana.com/oss/tempo/) ve [Jaeger](https://www.jaegertracing.io/) gibi backend'ler, trace'leri servis ve ortama göre gruplamak için bu nitelikleri kullanır. `sampling.probability`'yi `1.0` olarak ayarlamak geliştirme sırasında tüm istekleri yakalar, ancak üretim sistemleri depolama maliyetlerini kontrol etmek için genellikle %1 ile %10 arasında örnekleme yapar. ## Düşük ve Yüksek Kardinalite Etiketlerle Özel Gözlemler Oluşturma Gözlemler iki etiket türünü destekler: metrikler için düşük kardinalite (HTTP metodları veya durum kodları gibi sınırlı değerler) ve trace'ler için yüksek kardinalite (kullanıcı ID'leri veya istek ID'leri gibi sınırsız değerler). ```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()) // sınırlı: USD, EUR, GBP .highCardinalityKeyValue("payment.id", request.getPaymentId()) // istek başına benzersiz .highCardinalityKeyValue("customer.id", request.getCustomerId()) // yüksek kardinalite .observe(() -> executePayment(request)); } private PaymentResult executePayment(PaymentRequest request) { // Ödeme gateway çağrısı return new PaymentResult(true, "TXN-" + UUID.randomUUID()); } } ``` Düşük kardinalite etiketleri hem metriklerde hem de trace'lerde görünür. Yüksek kardinalite etiketleri yalnızca trace'lerde görünür çünkü sınırsız boyutlara sahip metrikler depolamayı patlatır. Bu ayrım, trace detaylarını zengin tutarken Prometheus kardinalite bombalarını önler. > **Mülakat Sorusu: Etiket Kardinalitesi** > > Mülakatçılar sıklıkla bazı etiketlerin neden metriklerden hariç tutulması gerektiğini sorar. Cevap kardinalite ile ilgilidir: bir metrik üzerindeki kullanıcı ID etiketi, kullanıcı başına bir zaman serisi oluşturur; potansiyel olarak milyonlarca seri. İzleme backend'leri yüksek kardinalite ile zorlanır, bu da bellek tükenmesine ve yavaş sorgulara yol açar. Trace'ler örnekleme yoluyla yüksek kardinality'yi yönetir, bu da onları istek spesifik tanımlayıcılar için doğru yer yapar. ## Asenkron Sınırlar Arasında Trace Bağlamını Yayma Dağıtık izleme bağlam yayılımı gerektirir. HTTP başlıkları servisler arasında trace ID'lerini taşır, ancak `@Async` metodları veya `CompletableFuture` zincirleri gibi asenkron işlemler yapılandırılmadığında bağlamı kaybedebilir. Spring Boot 3.4, yapılandırıldığında reaktif akışlar ve `@Async` metodları için otomatik yayılım sağlar: ```yaml # application.yml spring: reactor: context-propagation: auto task: execution: propagate-context: true ``` Özel `TaskExecutor` bean'leri için bunları `ContextPropagatingTaskDecorator` ile sarın: ```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, mevcut `Observation` ve trace bağlamını asenkron thread'e kopyalar. Bu olmadan, asenkron görevlerde başlatılan span'ların ebeveyni olmaz ve trace ağacı bozulur. ## @Observed ve @NewSpan Anotasyonlarını Kullanma Spring Boot 3.4, anotasyonlar aracılığıyla deklaratif gözlemlenebilirliği destekler. AspectJ weaver ekleyerek anotasyon işlemeyi etkinleştirin: ```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) { // Veritabanı sorgusu return new StockLevel(productId, 42); } @NewSpan("inventory.reserve") // Mevcut trace altında çocuk span oluşturur public ReservationResult reserveStock(String productId, int quantity) { // Envanter güncelleme return new ReservationResult(true, "RES-" + System.currentTimeMillis()); } } ``` `@Observed` hem bir metrik (timer) hem de bir span oluşturur. `@NewSpan` yalnızca span oluşturur, işlemin zaten başka yerde metrikleri olduğunda kullanışlıdır. Her iki anotasyon da istisna kaydı ve span durumunu otomatik olarak yönetir. ## Spring Boot 3.4'te Otomatik Enstrümante Edilen Bileşenler Spring Boot 3.4, kod değişikliği olmadan birkaç bileşeni otomatik enstrümante eder: | Bileşen | Gözlem Adı | Ne Yakalar | |---------|------------|------------| | Spring MVC | `http.server.requests` | İstek yolu, metod, durum, istisna | | WebClient | `http.client.requests` | Giden HTTP çağrıları | | RestClient | `http.client.requests` | Giden HTTP çağrıları (Spring 6.1+) | | Spring Kafka | `spring.kafka.listener` | Tüketici grubu, topic, partition | | Spring Data JPA | `spring.data.repository` | Repository metodu, sorgu süresi | | Scheduled Tasks | `spring.scheduling` | Görev adı, yürütme süresi | [Spring Boot Actuator dokümantasyonu](https://docs.spring.io/spring-boot/reference/actuator/observability.html) tüm otomatik enstrümante edilen bileşenleri listeler. [Datasource Micrometer](https://github.com/jdbc-observations/datasource-micrometer) gibi üçüncü parti kütüphaneler JDBC sorgu izleme ekler. ## Gürültüyü Azaltmak için Gözlemleri Filtreleme Sağlık kontrolleri, hazırlık probları ve statik kaynaklar trace'lerde gürültü üretir. Bunları yüklemler kullanarak filtreleyin: ```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; } } ``` Alternatif olarak, yapılandırmada ada göre gözlemleri devre dışı bırakın: ```yaml # application.yml management: observations: enable: spring.security: false # Spring Security gözlemlerini devre dışı bırak http.server.requests.actuator: false # Özel yüklem adı ``` > **Mülakat Bağlamı: İzleme Örnekleme vs. Filtreleme** > > Örnekleme, tüm endpoint'lerden toplanan trace yüzdesini azaltır. Filtreleme, belirli endpoint'leri tamamen kaldırır. Asla tanısal değer sağlamayan endpoint'ler için filtreleme kullanın (sağlık kontrolleri, metrik kazıma). Temsili verileri korurken maliyetleri kontrol etmek için örnekleme kullanın. ## Logları Trace ID'leriyle İlişkilendirme Spring Boot 3.4, Micrometer Tracing aktif olduğunda trace ve span ID'lerini MDC'ye (Mapped Diagnostic Context) otomatik olarak ekler. Logback ve Log4j2 bu ID'leri log çıktısına dahil edebilir: ```xml %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - traceId=%X{traceId} spanId=%X{spanId} - %msg%n ``` Bu kalıp ile her log satırı trace ID'sini içerir. Trace ID'ye göre log arama, tüm servislerden tek bir isteğin tüm mesajlarını döndürür ve Jaeger veya Tempo'daki span'lerle logları ilişkilendirir. ## Spring Boot Gözlemlenebilirliği Hakkında Yaygın Mülakat Soruları Mülakatçılar hem kavramsal anlayışı hem de gözlemlenebilirlikle pratik deneyimi değerlendirir. Bu sorular senior backend pozisyonlarında sıkça karşılaşılır. **S: Micrometer Tracing ile OpenTelemetry arasındaki fark nedir?** Micrometer Tracing, dağıtık izleme için vendor-neutral bir API'dir, Micrometer'ın metrikleri soyutlamasına benzer. OpenTelemetry belirli bir uygulama ve protokoldür. Spring Boot, Micrometer Tracing'i API olarak kullanır ve `micrometer-tracing-bridge-otel` aracılığıyla export için OpenTelemetry'ye köprüler. Bu katmanlama, uygulamaların kod değişikliği olmadan backend'leri değiştirmesine olanak tanır. **S: Spring neden doğrudan OpenTelemetry enstrümantasyonu yerine Observation API'yi önerir?** Observation API, hem metrikler hem de trace'ler üreten tek bir enstrümantasyon noktası sağlar. Doğrudan OpenTelemetry çağrıları yalnızca trace'ler üretir. `Observation.observe()` kullanmak aynı koddan bir timer metriği ve bir span üretir, tekrarı azaltır ve tutarlı adlandırma sağlar. **S: Span'lar arasında boşluk gösteren bir trace'i nasıl debug edersiniz?** Boşluklar eksik enstrümantasyonu veya kopmuş bağlam yayılımını gösterir. Kodun `ContextPropagatingTaskDecorator` olmadan asenkron işlemler kullanıp kullanmadığını kontrol edin. HTTP istemcilerinin enstrümante edildiğini doğrulayın (WebClient, RestClient veya manuel olarak sarılmış RestTemplate). Mesaj kuyrukları için, trace başlıklarının mesaj özellikleri aracılığıyla yayıldığını onaylayın. **S: Bir metriğe yüksek kardinalite etiket eklerseniz ne olur?** Her benzersiz etiket değeri yeni bir zaman serisi oluşturur. Milyonlarca kullanıcıya sahip bir kullanıcı ID etiketi milyonlarca seri oluşturur ve Prometheus veya diğer TSDB backend'lerinde belleği tüketir. Çözüm, yüksek kardinalitenin beklendiği yalnızca trace'lere etiketi ekleyen `highCardinalityKeyValue()` yerine `lowCardinalityKeyValue()` kullanmaktır. ## Docker Compose ile Dağıtık İzleme Pipeline'ı Oluşturma Yerel bir gözlemlenebilirlik stack'i, üretime dağıtımdan önce enstrümantasyonu doğrulamaya yardımcı olur. Bu örnek [OpenTelemetry Collector](https://opentelemetry.io/docs/collector/) ve Jaeger kullanır: ```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, Spring Boot uygulamasından span'leri alır ve Jaeger'a iletir. Bu mimari, uygulama kodunu değiştirmeden exporter'lar (Tempo, Zipkin, bulut backend'leri) eklemeye olanak tanır. ## Spring Boot Gözlemlenebilirliği için Temel Çıkarımlar - Observation API metrikleri ve izlemeyi birleştirir: tek bir `Observation.observe()` çağrısı hem timer metriği hem de span üretir, çift enstrümantasyonu ortadan kaldırır. - `micrometer-tracing-bridge-otel` ve `opentelemetry-exporter-otlp` eklemek OTLP exportunu etkinleştirir. Spring Boot 3.4, HTTP exporter'ı `management.otlp.tracing.endpoint`'ten aldığı endpoint ile otomatik yapılandırır. - Metriklerde görünen boyutlar için `lowCardinalityKeyValue()` kullanın (sınırlı değerler). Yalnızca trace verileri için `highCardinalityKeyValue()` kullanın (kullanıcı ID'leri, istek ID'leri). - `spring.task.execution.propagate-context=true` ile asenkron kod için bağlam yayılımını etkinleştirin ve özel executor'ları `ContextPropagatingTaskDecorator` ile sarın. - `/actuator/health` gibi gürültülü endpoint'leri `ObservationPredicate` bean'leri kullanarak filtrelemek trace'leri iş operasyonlarına odaklanmış tutar. - Log kalıpları, gözlemlenebilirlik backend'inizdeki dağıtık trace'lerle log satırlarını ilişkilendirmek için `%X{traceId}` içermelidir. - Gözlemlenebilirlik hakkındaki mülakat soruları kardinalite, bağlam yayılım hataları ve Observation API ile doğrudan OpenTelemetry kullanımı arasındaki ayrıma odaklanır. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/tr/blog/spring-boot/spring-boot-observability-opentelemetry-distributed-tracing