Java backend geliştirme sistemlerinde istek, kuyruk, veritabanı ve AI araç çağrılarını aynı iz altında birleştirmek için Spring Boot, OpenTelemetry, örnekleme ve JFR tabanlı maliyet ölçümü tasarımı.
Spring Boot'ta OpenTelemetry ile Uçtan Uca İz Sürme Tasarımı
Spring Framework'te trace sınırları ve adlandırma sözleşmesi
Dağıtık izleme, yalnızca her HTTP isteğine bir traceId yazmak değildir. W3C Trace Context standardındaki traceparent başlığı, çağrının trace kimliğini, parent span kimliğini ve örnekleme bayrağını taşır. Spring Framework uygulamasında internetten gelen bu başlığı doğrudan güvenilir kabul etmeyin: API gateway, doğrulanmamış istemcilerin çok büyük veya biçimsiz header göndermesini sınırlamalı; dış sınırda yeni bir sunucu span'ı açmalıdır. Servis adlarını checkout-api, span adlarını ise düşük kardinaliteli fiil-nesne biçiminde POST /orders, inventory.reserve olarak sabitleyin. orderId=... gibi her istekte değişen değerleri span adına koymak, trace backend'indeki grup sayısını gereksiz biçimde büyütür.
Bir java eğitimi veya java programlama eğitimi içinde izleme anlatılırken, öğrencilere önce şu envanteri çıkartmak daha yararlıdır: HTTP girişleri, Kafka producer-consumer sınırları, SQL çağrıları ve üçüncü taraf HTTP bağımlılıkları. Bu yaklaşım, yalnızca bir java kursu laboratuvarı için değil, java fullstack eğitimi alan bir geliştiricinin tarayıcıdaki traceparent başlığından backend span ağacına kadar sorun takip etmesi için de uygulanabilir bir sözleşmedir. Örneğin her servis deposuna aşağıdaki adlandırma tablosunu koyun:
- HTTP: method ve route template, örnek
GET /orders/{id} - Mesajlaşma:
orders publishveorders process - Uygulama işi:
pricing.calculate - Hata: istisna sınıfı attribute olarak, ham hata mesajı değil
Java microservices içinde W3C bağlamını async işe taşımak
OpenTelemetry Java agent veya OpenTelemetry Spring Boot starter kullanıyorsanız HTTP istemcileri ve yaygın executor'lar çoğu durumda otomatik enstrümante edilir. Ancak uygulamanın kendi ThreadPoolTaskExecutor bean'i, özel kuyruk adaptörü veya elle oluşturulan ExecutorService kullanıldığında bağlamı açıkça sarmalamanız gerekir. Aksi halde async iş yeni bir root trace altında görünür; üretimde bu hata, bir sipariş isteğinin 12 saniye sürdüğü halde üç ayrı trace olarak görünmesine yol açar. Aşağıdaki TaskDecorator, submit anındaki Context nesnesini yakalar ve worker thread üzerinde scope olarak açar.
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import org.springframework.core.task.TaskDecorator;
final class OtelTaskDecorator implements TaskDecorator {
@Override
public Runnable decorate(Runnable task) {
Context submitterContext = Context.current();
return () -> {
try (Scope scope = submitterContext.makeCurrent()) {
task.run();
}
};
}
}
@Bean
ThreadPoolTaskExecutor pricingExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(16);
executor.setMaxPoolSize(64);
executor.setQueueCapacity(500);
executor.setTaskDecorator(new OtelTaskDecorator());
executor.initialize();
return executor;
}Buradaki kritik ayrıntı, transaction context ile trace context'in aynı şey olmamasıdır. Bir @Transactional EntityManager'ını @Async işine taşımayın; Hibernate ORM oturumları thread-safe değildir ve transaction zaten çağıran thread'de tamamlanmış olabilir. Async iş, yalnızca trace context'i devralmalı; kendi transaction sınırını açmalıdır. Bunu test etmek için bir controller içinde pricingExecutor.execute(...) çağırın, ardından Jaeger, Grafana Tempo veya başka bir OTLP uyumlu backend'de async span'ın parentSpanId değerinin HTTP server span'ına bağlandığını doğrulayın.
Hibernate ORM ve Spring Data JPA sorgularını izlerle eşlemek
Hibernate ORM ve Spring Data JPA kullanan servislerde JDBC span'ı tek başına yeterli bağlam sağlamaz: SQL span'ının hangi iş kuralına ait olduğunu görebilmek için servis katmanında anlamlı bir iç span açın. Aşağıdaki örnekte span adı sabittir; sipariş kimliği span adına değil, yalnızca güvenli olduğu doğrulanmış attribute olarak eklenir. Kimlik müşteri numarası, e-posta veya erişim token'ı içeriyorsa attribute olarak dahi göndermeyin.
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.StatusCode;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Scope;
import org.springframework.transaction.annotation.Transactional;
@Transactional(readOnly = true)
public List<Order> findOpenOrders(String tenantCode) {
Span span = tracer.spanBuilder("order.find_open")
.setAttribute("app.tenant_tier", tenantTier(tenantCode))
.startSpan();
try (Scope ignored = span.makeCurrent()) {
return orderRepository.findByTenantCodeAndStatus(tenantCode, Status.OPEN);
} catch (RuntimeException ex) {
span.recordException(ex);
span.setStatus(StatusCode.ERROR);
throw ex;
} finally {
span.end();
}
}JDBC enstrümantasyonu açıkken SQL metni ve bind parametreleri veri sızıntısı riski taşır. Özellikle WHERE email = ? biçimindeki sorguda parametre görünürlüğünü açmak, observability sistemini kişisel veri deposuna dönüştürebilir. Spring ayarlarında yavaş sorgu eşiğini ayrı tutun ve SQL parametrelerini trace attribute'u olarak eklemeyin:
spring:
jpa:
properties:
hibernate.session.events.log.LOG_QUERIES_SLOWER_THAN_MS: 200
hibernate.generate_statistics: false Eşik aşımı logunu traceId ile ilişkilendirmek için uygulama log formatına MDC alanı ekleyin. Ancak hibernate.generate_statistics değerini sürekli yüksek trafikli üretim ortamında açık bırakmadan önce allocation ve lock maliyetini ölçün. Spring Data JPA tarafında repository metodunun açtığı sorgu sayısını trace içindeki child JDBC span sayısıyla kontrol etmek, beklenmeyen lazy load zincirlerini istemci gecikmesiyle doğrudan eşleştirir.Java backend geliştirme için örnekleme ve JFR ile maliyet ölçümü
Tüm trace'leri kalıcı olarak dışa aktarmak yerine, parent-based head sampling ile başlangıçta düşük bir oran seçin ve hata veren trace'leri collector katmanında saklayacak tail-sampling politikası kullanın. Uygulamada Java agent ile enstrümantasyon yapıyorsanız starter ile ikinci kez otomatik enstrümantasyon eklemeyin; çift kurulum aynı HTTP veya JDBC operasyonu için iç içe, yanıltıcı span'lar üretir. Aşağıdaki yapılandırma yüzde 5 yeni trace örnekler, parent örneklenmişse aynı kararı çocuk span'lara korur ve batch exporter kuyruğunu sınırlar.
export OTEL_SERVICE_NAME=checkout-api
export OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4318
export OTEL_TRACES_SAMPLER=parentbased_traceidratio
export OTEL_TRACES_SAMPLER_ARG=0.05
export OTEL_BSP_SCHEDULE_DELAY=5000
export OTEL_BSP_MAX_QUEUE_SIZE=2048
java -javaagent:/opt/otel/opentelemetry-javaagent.jar -jar app.jarBu değişikliğin maliyetini tahmin etmeyin, karşılaştırın. Aynı veri kümesi ve aynı yük profiliyle k6 veya Gatling çalıştırmasını iki kez yürütün: önce agent kapalı, sonra agent ve yüzde 5 sampling açık. Her koşulda p50, p95, p99 gecikme, işlem başına allocation ve CPU oranını kaydedin. JVM tarafındaki örnekleme için 120 saniyelik JFR kaydı alın:
jcmd $PID JFR.start name=otel settings=profile duration=120s filename=/tmp/otel.jfr Java Mission Control içinde Socket Write, Object Allocation in New TLAB ve exporter thread'lerinin CPU örneklerini karşılaştırın. Exporter kuyruk doluyorsa uygulama isteğini bekletmek yerine span düşürme davranışı görülmelidir; bu nedenle collector'a erişim kesildiğinde kuyruk uzunluğu, dropped span sayısı ve p99 değerini ayrıca test edin. Bu ölçüm, spring boot eğitimi içeriğinde 'izleme ekledik' demekten daha değerli bir kapasite kabul kriteridir.Spring AI ve Spring MCP çağrılarında hassas veri sınırı
Spring AI ile model çağrısı yaparken trace'e prompt, tamamlanmış yanıt veya ham tool argümanı yazmak varsayılan bir gözlemlenebilirlik stratejisi olmamalıdır. Bu alanlar müşteri verisi, kaynak kod veya erişim bilgisi taşıyabilir. Bunun yerine model adı, sağlayıcı adı, yanıt durumu, token sayısı ve gecikme gibi sınırlı alanları kaydedin. Spring MCP kullanan bir istemcide de model context protocol üzerinden yapılan araç çağrısını ayrı bir span olarak adlandırın; örneğin mcp.tool.inventory_lookup. Araç adı düşük kardinalitelidir, fakat kullanıcı tarafından üretilmiş JSON argümanı değildir.
Span span = tracer.spanBuilder("mcp.tool.inventory_lookup").startSpan();
try (Scope ignored = span.makeCurrent()) {
span.setAttribute("gen_ai.provider.name", "internal");
span.setAttribute("mcp.tool.name", "inventory_lookup");
span.setAttribute("mcp.argument_bytes", requestJson.length());
return mcpClient.callTool("inventory_lookup", requestJson);
} catch (RuntimeException ex) {
span.recordException(ex);
span.setStatus(StatusCode.ERROR);
throw ex;
} finally {
span.end();
}Spring AI, spring mcp ve model context protocol eğitimlerinde gözden kaçan nokta şudur: trace sistemi de bir veri dışa aktarım kanalıdır. OTLP collector'a giden ağ segmentini, erişim rollerini ve saklama süresini uygulama loglarından bağımsız belirleyin. Testte kasıtlı olarak e-posta ve API anahtarı benzeri bir prompt gönderin; collector'daki span attribute'larında bu dizelerin bulunmadığını otomatik bir entegrasyon testiyle doğrulayın. Bu kontrol, java microservices içindeki her aracın aynı veri sınıflandırma kuralına uyup uymadığını denetlenebilir hale getirir.
İ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 sırasında OpenTelemetry Java agent mi yoksa starter mı seçilmeli?
Önce otomatik enstrümantasyon kapsamını envanterleyin. Java agent, JVM başlatılırken bytecode enstrümantasyonu yaparak HTTP, JDBC ve yaygın client kütüphanelerini kod değişikliği olmadan kapsar. Starter ise Spring bean'leri ve yapılandırması üzerinde daha kontrollü davranır. Aynı uygulamada ikisini körlemesine açmayın; Tempo veya Jaeger'de tek bir HTTP isteği için iki server span görüyorsanız çift enstrümantasyon vardır.
Spring Data JPA ve Hibernate ORM izlerinde SQL parametreleri neden görünmemeli?
Bind parametreleri e-posta, telefon, adres, token veya ticari veri içerebilir ve OTLP exporter bunları uygulama sınırının dışına taşır. Sorgu sınıfını repository metodu veya sabit span adıyla izleyin, yavaş sorguları 200 ms gibi ölçülebilir eşikle loglayın. Sorgu planı gerektiğinde üretim trace'inden parametre kopyalamak yerine, maskelenmiş değerlerle kontrollü bir veritabanı oturumunda EXPLAIN ANALYZE çalıştırın.
Java backend geliştirme ortamında OpenTelemetry maliyeti nasıl ölçülür?
Agent kapalı ve açık iki koşulda aynı k6 veya Gatling senaryosunu çalıştırın. p95-p99 gecikme, CPU, allocation rate ve dropped span sayısını kaydedin. Her koşulda jcmd ile 120 saniyelik JFR alın; Java Mission Control'de exporter kaynaklı socket write, allocation ve CPU örneklerini karşılaştırın. Böylece maliyetin HTTP enstrümantasyonundan mı, fazla attribute üretiminden mi, yoksa erişilemeyen collector nedeniyle dolan kuyruktan mı geldiği ayrıştırılabilir.
Spring AI ve Spring MCP model context protocol trace'lerinde hangi alanlar saklanmalı?
Araç adı, model veya sağlayıcı adı, çağrı süresi, sonuç kodu ve sayısal token metriği saklanabilir. Prompt, model cevabı, ham tool argümanı, authorization header ve kullanıcı kimliği saklanmamalıdır. Özellikle tool argümanını kaydetmek yerine byte uzunluğu veya önceden tanımlı işlem türü gibi düşük riskli, düşük kardinaliteli alanları kullanın.
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.


