Trace ID Yetmediğinde: Distributed Tracing’de Baggage Kullanımı

Merhaba Arkadaşlar,

Geçtiğimiz yıllarda, hepimizin eve kapanmak zorunda kaldığı o sıcak günlerde, çalıştığım şirketteki yemek sipariş uygulamasında yaşanan bir sipariş problemini incelerken uzun zamandır gözümüzün önünde duran bir eksikliği fark etmiştik. Bu yazıda, o problem üzerinden W3C tarafından standartlaştırılan Baggage yapısına ve kullanımına değineceğim.

Senaryo basit;

Kullanıcı siparişinin ödemesini yapmış fakat kurye ataması yapılmamış. Destek ekibi ilgili işlemin traceId bilgisini iletti, biz de APM üzerinden trace’i takip etmeye başladık.

Order-service tarafında her şey normal görünüyordu. Sipariş oluşturulmuş, event RabbitMQ’ya gönderilmiş.

Payment-service tarafına geçtiğimizde ise aynı traceId ile herhangi bir kayıt bulamadık.

Burada sebep mesajlaşma tarafındaki context propagation’ın eksik olmasıydı. HTTP request boyunca taşıdığımız trace context, RabbitMQ üzerinden devam eden işlemde düzgün propagate edilmediği için consumer tarafında yeni bir trace oluşmuştu.

Peki elimizde ne vardı?

Sipariş numarası.

Fakat o da structured bir log alanı değildi. Log mesajlarının içerisinde text olarak geçiyordu. Elasticsearch üzerinde sipariş numarasını aratarak ilgili kaydı bulduk ama basit bir problemi takip etmek yaklaşık 20 dakika sürdü.

Aslında sorun şuydu; teknik olarak işlemi takip edebileceğimiz bir traceId vardı, iş tarafında ise sipariş numaramız vardı fakat ikisini servisler arasında birlikte taşımıyorduk.

İşte tam bu noktada Baggage kullanışlı hale geliyor.

Trace ID ve Baggage Arasındaki Fark

Distributed tracing kullandığımız bir sistemde traceId, bir isteğin servisler arasında nasıl ilerlediğini takip etmemizi sağlar.

API Gateway
|
v
Order Service
|
v
Payment Service
|
v
Courier Service

Context doğru propagate edildiğinde bütün bu akışı aynı trace üzerinden takip edebiliriz.

Fakat bizim için bazen sadece teknik trace yeterli olmayabilir.

Sipariş numarası, müşteri numarası veya shipment ID gibi business tarafında anlamlı başka bir identifier üzerinden de arama yapmak isteyebiliriz.

Baggage tam olarak bunun için kullanabileceğimiz mekanizmalardan biri.

W3C tarafında baggage isimli ayrı bir header bulunuyor ve içerisinde key-value şeklinde bilgiler taşınabiliyor.

baggage: order.no=ORD-2026-88213

Böylece order.no bilgisini downstream servislerde de okuyabiliyoruz.

Tabii burada önemli bir nokta var;

Baggage, trace propagation problemini sihirli bir şekilde çözmüyor.

RabbitMQ, Kafka, HTTP veya kullandığınız başka bir transport üzerinde context propagation’ın yine doğru yapılandırılmış olması gerekiyor. Baggage da bu propagation mekanizması üzerinden taşınıyor.

Micrometer Tracing ile Baggage Kullanımı

Micrometer Tracing tarafında baggage oluşturmak oldukça basit.

Örnek verecek olursak;

tracer.createBaggageInScope("order.no", "ORD-2026-88213").use {
    // sipariş işleme
}

Bu scope içerisinde baggage değerine ulaşabiliriz.

var baggage = tracer.getBaggage("order.no");

if (baggage != null) {
    System.out.println(baggage.get());
}

Fakat burada kullandığınız propagation tipine dikkat etmek gerekiyor.

Spring Boot tarafında baggage alanlarını remote sistemlere propagate etmek istediğimizde ilgili alanları konfigürasyon üzerinden de açıkça tanımlayabiliyoruz.

management:
  tracing:
    baggage:
      remote-fields:
        - order.no
      correlation:
        fields:
          - order.no

Burada iki farklı ayar var.

remote-fields, ilgili baggage bilgisinin servis sınırlarının dışına taşınmasını sağlar.

correlation.fields ise baggage içerisindeki değeri MDC’ye ekler.

Bu da bizim için oldukça kullanışlı çünkü her log satırında manuel olarak;

log.info("orderNo={} payment started", orderNo);

yazmak yerine MDC üzerinden otomatik olarak log pattern’ine ekleyebiliriz.

Örneğin loglarımız şu hale gelebilir;

2026-08-10 14:32:01
traceId=af53...
order.no=ORD-2026-88213
Payment completed

Artık Elasticsearch veya kullandığımız başka bir log platformunda doğrudan;

order.no:"ORD-2026-88213"

şeklinde arama yapabiliriz.

Bizim yaşadığımız problemde ihtiyacımız olan şey aslında tam olarak buydu.

RabbitMQ Tarafı

HTTP tarafında Spring Boot’un sağladığı instrumentation sayesinde trace propagation çoğu zaman farkında olmadan çalışıyor.

Messaging tarafında ise kullandığımız kütüphaneye ve instrumentation’a göre durum değişebiliyor.

Mesaj publish edilirken tracing context’in message header’larına yazılması, consumer tarafında da bu context’in tekrar okunması gerekiyor.

Order Service

traceId: 123
order.no: ORD-88213

|
| RabbitMQ
|
v

Payment Service

traceId: 123
order.no: ORD-88213

Eğer tracing context propagate edilmezse consumer tarafında yeni bir trace başlayabilir.

Bu durumda sipariş numarası gibi business identifier’ları ayrıca loglarda tutmak problemi bulmamızı yine kolaylaştırır fakat ideal olan hem trace context’in hem de gerekli business context’in doğru şekilde taşınmasıdır.

Her Şeyi Baggage’a Koymalı mıyız?

Hayır 🙂

Baggage kullanışlı olduğu kadar kontrolsüz kullanıldığında problem oluşturabilecek bir yapı.

Örneğin aşağıdaki bilgileri baggage içerisinde taşımak iyi bir fikir değil;

creditCardNumber
customerAddress
email
phoneNumber

Baggage servisler arasında taşındığı için hassas veya kişisel verilerin buraya koyulmaması gerekiyor.

Bizim ihtiyacımız daha çok;

order.no
shipment.id
tenant.id

gibi sistemi debug ederken veya operasyonel olarak bir işlemi takip ederken kullanabileceğimiz identifier’lar.

Bir diğer önemli konu da security.

Baggage içerisinden gelen bir değere bakarak authorization kararı vermemek gerekiyor. Çünkü baggage bir security mekanizması değil, request context taşıma mekanizmasıdır.

İsimlendirme

Küçük sistemlerde orderNo gibi bir isim çok problem olmayabilir.

Fakat servis sayısı arttıkça baggage key’leri de ortak bir namespace gibi kullanılmaya başlanıyor.

Bu nedenle ben;

order.no
shipment.id
customer.id

gibi domain’i belli eden isimleri tercih etmeyi daha doğru buluyorum.

İleride başka bir servisin id, orderId veya number gibi generic bir baggage alanı eklemesi durumunda neyin ne olduğunu anlamaya çalışmak zorunda kalmıyoruz.

Sonuç

Distributed tracing bize sistem içerisindeki teknik akışı takip etmek için oldukça güçlü bir imkan sağlıyor.

Fakat production’da bir problemi araştırırken destek ekibi genellikle bize “traceId nedir?” diye gelmiyor.

Daha çok “ORD-2026-88213 numaralı sipariş neden ilerlemedi?” diye geliyor 🙂

Bu nedenle bazı business identifier’larını trace context ile birlikte taşıyabilmek ve structured olarak loglamak ciddi anlamda işimizi kolaylaştırıyor.

Bizim senaryoda RabbitMQ tarafındaki context propagation eksikliğini düzelttik ve sipariş numarasını da correlation alanı olarak loglara ekledik.

Böylece artık bir siparişi servisler arasında takip etmek için log mesajlarının içerisinde text search yapmak zorunda kalmıyoruz.

20 dakikalık Elasticsearch macerasından sonra insan bunun değerini daha iyi anlıyor 🙂

Bir sonraki yazıda görüşmek üzere.

Kaynaklar