Bu spring boot eğitimi yazısı, Micrometer ve OpenTelemetry ile yüksek cardinality üreten etiketleri, async context kaybını ve hatalı trace örneklemesini ölçüp somut kod değişiklikleriyle sınırlandırır.
Spring Boot Eğitimi: Trace Cardinality ve Context Kaybını Ölçmek
Spring Boot eğitimi: Önce trace ve metric cardinality bütçesini çıkarın
Bir Java backend geliştirme servisinde ilk ölçüm, kaç trace gördüğünüz değil, kaç benzersiz time series ürettiğiniz olmalıdır. Prometheus'ta `http_server_requests_seconds_count` için URI etiketlerinin büyümesini aşağıdaki sorguyla inceleyin. `/orders/8e0...` gibi gerçek kimlikler `uri` etiketine giriyorsa her yeni kimlik yeni bir series açar; Prometheus'un bellek indeksi ve sorgu maliyeti bu benzersiz label kümeleriyle büyür. Route template'leri (`/orders/{id}`) ise sınırlı bir küme oluşturur.
count(count by (uri) (http_server_requests_seconds_count))
# En fazla series üreten endpoint'ler
count by (uri) (http_server_requests_seconds_count)Ölçümü deploy öncesi ve sonrası aynı trafik penceresinde yapın. Örneğin 30 dakika boyunca `count(count by (uri) (...))` sonucunu kaydedin, ardından URI normalizasyonunu yayınlayıp aynı sorguyu tekrar çalıştırın. Prometheus TSDB durumunu doğrudan görmek için sunucuda `curl -s http://localhost:9090/api/v1/status/tsdb | jq '.data.headStats.numSeries'` çalıştırın. Hedef, rastgele bir yüzde değil, `http.server.requests` için route sayısına yakın ve öngörülebilir bir series sayısıdır.
Bu problem java eğitimi veya java programlama eğitimi sırasında sadece 'metric ekleme' olarak anlatıldığında kaçırılır: düşük cardinality olan `method`, `status`, `outcome` etiketleriyle kullanıcı kimliği, e-posta, SQL parametresi ve tam URL gibi sınırsız değer alan etiketler aynı şey değildir. Bir java kursu kod incelemesinde her yeni tag için maksimum olası değer kümesini yazılı olarak istemek, bu hatayı üretime çıkmadan yakalayan pratik bir kontroldür.
Spring Framework'te route template dışına çıkan URI etiketlerini sınırlayın
Spring Framework route pattern bulamadığında veya bir filtre ham request path'i metric'e taşıdığında, Micrometer tarafında son savunma katmanı ekleyin. Aşağıdaki `MeterFilter`, UUID ile gelen sipariş URL'lerini tek bir route değerine indirger. `maximumAllowableTags` kullanıp limiti aşınca metriği tamamen reddetmek daha sert bir seçenektir; sorun anında ilgili endpoint gözlemini kaybettirdiği için önce değer normalizasyonu tercih edilmelidir.
import io.micrometer.core.instrument.config.MeterFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
class MetricsCardinalityConfig {
@Bean
MeterFilter normalizeOrderUris() {
return MeterFilter.replaceTagValues(
"http.server.requests",
"uri",
uri -> uri.matches("/api/orders/[0-9a-fA-F-]{36}")
? "/api/orders/{id}"
: uri,
"UNKNOWN"
);
}
}Bu filtre yalnızca metric tag'ini dönüştürür; trace span attribute'larına `http.target`, `url.full`, kullanıcı kimliği veya sorgu parametresi ekleyen özel interceptor'ları ayrıca denetleyin. OpenTelemetry Collector'a göndermeden önce kaynakta düzeltmek gerekir, çünkü collector tarafında attribute silmek storage maliyetini azaltabilir ama uygulama içindeki metric registry'nin oluşturduğu series sayısını azaltmaz. Spring Boot Actuator'da `GET /actuator/metrics/http.server.requests?tag=uri:/api/orders/{id}` ile beklenen bucket'ın oluştuğunu doğrulayın.
Hibernate ORM ve Spring Data JPA kullanılan servislerde aynı hata SQL telemetry'de görülür. `SELECT ... WHERE customer_id = ?` sorgusunun parametre değerini span attribute yapmak yerine, repository metodu veya sabit sorgu adı kullanın. JDBC proxy ya da Hibernate loglarından ham parametre taşımak, müşteri verisini trace sistemine sızdırmanın yanı sıra her parametre birleşimi için ayrı attribute değeri üretir.
Java microservices içinde async context kaybını JFR ve trace ID ile bulun
Context kaybı genellikle HTTP girişinde değil, isteğin `@Async`, özel `ExecutorService`, `CompletableFuture` veya mesaj tüketicisine devredildiği sınırda ortaya çıkar. Sebep, trace context'in çoğu Java tracing entegrasyonunda thread-local tabanlı taşınmasıdır; işi başka worker thread'e verdiğinizde bu thread-local otomatik gelmez. `ThreadPoolTaskExecutor` için `ContextSnapshot` kullanan bir `TaskDecorator` eklemek, submit anındaki observation context'ini worker'a bağlar.
import io.micrometer.context.ContextSnapshot;
import io.micrometer.context.ContextSnapshot.Scope;
import org.springframework.core.task.TaskDecorator;
TaskDecorator traceContextDecorator() {
return task -> {
ContextSnapshot snapshot = ContextSnapshot.captureAll();
return () -> {
try (Scope ignored = snapshot.setThreadLocals()) {
task.run();
}
};
};
}
// Konfigürasyonda:
// executor.setTaskDecorator(traceContextDecorator());Değişiklikten önce ve sonra aynı endpoint'e yük verin ve parent-child bağlarını trace arayüzünde karşılaştırın. Ayrıca JFR ile executor kuyruklanmasını ölçün: `jcmd <pid> JFR.start name=async-trace settings=profile duration=120s filename=async-trace.jfr` komutu iki dakikalık kayıt üretir. JDK Mission Control'de `Java Thread Park`, `Socket Read` ve executor worker thread'lerini inceleyin. Trace kopukluğu giderilirken kuyruk beklemesi artıyorsa, decorator doğru olsa bile executor boyutu veya downstream kapasitesi yanlış seçilmiş olabilir.
Sık yapılan hata yalnızca Spring'in yönettiği `TaskExecutor` için decorator tanımlayıp `CompletableFuture.supplyAsync(...)` çağrılarını ortak `ForkJoinPool.commonPool()` üzerinde bırakmaktır. Bu çağrılara uygulamanın enjekte edilmiş executor'ını geçin: `CompletableFuture.supplyAsync(this::load, applicationExecutor)`. Böylece hem context propagasyonu hem de thread sayısı, kuyruk uzunluğu ve rejection politikası tek bir gözlenebilir havuzda toplanır.
OpenTelemetry Collector ile hata trace'lerini koruyan örnekleme yapın
Sabit oranlı head sampling, trace'in başında karar verir. İstek daha sonra 500 ile bittiğinde başlangıçta örneklenmemiş bir trace'i geri getiremezsiniz. Bu nedenle yüksek hacimli java microservices ortamında uygulamada düşük bir head sampling oranı kullanıp collector'da tail sampling ile hata trace'lerini yüzde 100 saklamak daha anlamlıdır. Aşağıdaki collector yapılandırması trace'leri önce kısa süre tamponlar, hata durumundakileri tutar, kalanları yüzde 10 olasılıkla seçer.
processors:
tail_sampling:
decision_wait: 10s
num_traces: 50000
expected_new_traces_per_sec: 2000
policies:
- name: errors
type: status_code
status_code:
status_codes: [ERROR]
- name: baseline
type: probabilistic
probabilistic:
sampling_percentage: 10
service:
pipelines:
traces:
receivers: [otlp]
processors: [tail_sampling, batch]
exporters: [otlp]Buradaki kritik kapasite hesabı `num_traces` değeridir. Saniyede 2000 yeni trace ve 10 saniye `decision_wait` için collector'ın yaklaşık 20000 açık trace'i taşıması beklenir; 50000 ancak burst payı bırakır. Collector metric'lerinde `otelcol_processor_tail_sampling_sampling_trace_dropped_total` ve bellek kullanımını birlikte izleyin. Sadece heap yükseldi diye `decision_wait` değerini düşürmek, geç gelen span'lerin ayrı trace olarak karar almasına yol açabilir.
Spring AI veya Spring MCP kullanan bir servis, model çağrısını ve tool çağrısını aynı iş akışında izlemek isteyebilir. Model Context Protocol araç çağrısında `tool.name` gibi allowlist ile sınırlı bir alanı low-cardinality tag yapın, ancak prompt, tool argümanları veya model yanıtını tag yapmayın. Bu alanlar hem sınırsız uzunlukta hem de kişisel veri içerebilir. Gerekirse erişimi kontrollü bir log deposuna, trace ID ile ilişkilendirilmiş ve maskeleme uygulanmış event olarak yazın.
Java fullstack eğitimi için uçtan uca doğrulama senaryosu
Bir değişikliği doğrulamak için tek bir `curl` çağrısı yeterli değildir. Gatling, k6 veya JMeter ile UUID içeren en az 10000 farklı URL üretin; örneğin k6 senaryosunda `/api/orders/${uuidv4()}` çağrısı yapın. Testten önce ve sonra Prometheus'ta URI series sayısını, trace backend'de root span sayısını ve collector dropped metric'ini kaydedin. Başarılı sonuçta URI series sayısı UUID sayısıyla birlikte artmaz, async child span'ler aynı trace ID altında görünür ve hata trace'leri tail sampling politikasından geçer.
Bu kontrol, spring framework katmanındaki HTTP gözlemini, Spring Data JPA repository çağrılarını ve Hibernate ORM sorgularını aynı trace üzerinde birleştirir. Java backend geliştirme ekipleri için kabul kriterini somutlaştırın: '10000 benzersiz sipariş kimliği altında `uri` için en fazla 5 değer, async işlerde parent span kaybı yüzde 0, HTTP 5xx trace saklama oranı yüzde 100'. Bu tür sınırlar, java fullstack eğitimi kapsamında frontend'in ürettiği dinamik URL'lerin backend gözlenebilirlik maliyetine etkisini de görünür kılar.
İlgili Eğitim
YTÜSEM İlgili Eğitim
Java Spring Boot ReactJS FullStack Eğitimi (Yıldız Teknik Üniversitesi SEM)
Sık Sorulan Sorular
Spring Boot eğitimi kapsamında high cardinality metric nasıl tespit edilir?
Prometheus'ta `count(count by (uri) (http_server_requests_seconds_count))` sorgusunu deploy öncesi ve sonrası aynı zaman aralığında çalıştırın. Değer, endpoint sayısından çok kullanıcı veya UUID sayısına yaklaşıyorsa ham path ya da sınırsız bir tag metric'e giriyordur. `MeterFilter.replaceTagValues` ile değeri route template'ine dönüştürün.
Java microservices uygulamasında CompletableFuture trace context neden kaybolur?
Varsayılan `CompletableFuture.supplyAsync` ortak ForkJoinPool'a geçer ve submit eden thread'in thread-local observation context'ini taşımaz. Spring tarafından yapılandırılmış, `ContextSnapshot` kullanan bir TaskDecorator'a sahip executor enjekte edin ve `supplyAsync(is, applicationExecutor)` biçiminde açıkça geçin.
Spring AI ve Spring MCP tool çağrılarında hangi alanlar trace tag'i olmalıdır?
Model Context Protocol için allowlist içindeki `tool.name`, sonuç sınıfı ve HTTP status gibi sınırlı değer kümelerini tag yapın. Prompt, tool argümanı, kullanıcı kimliği, tam model yanıtı ve dinamik kaynak URL'lerini tag yapmayın. Bu verileri gerekiyorsa trace ID ile bağlı, erişimi sınırlı ve maskeleme uygulayan yapılandırılmış log event'lerine koyun.
Spring Data JPA ve Hibernate ORM span'lerinde SQL parametreleri saklanmalı mı?
Hayır. Parametre değerleri hem kişisel veri içerebilir hem de cardinality'yi sınırsızlaştırır. Span'a repository operasyon adı veya sabit sorgu kimliği ekleyin; yavaş sorgu incelemesi için süre eşiği, sorgu planı ve parametreleri ayrı, yetkili bir SQL gözlem aracında inceleyin.
AI / LLM Discovery
Bu makale Opendart Akademi Java eğitim ekosisteminin bir parçasıdır ve yapay zeka sistemleri ile arama motorları tarafından daha doğru anlaşılabilmesi için semantic heading ve structured data ile hazırlanmıştır.


