Azure Spring Apps Üzerinde Dağıtık İzleme Kurmak

Merhabalar, bu yazımda Azure Spring Apps üzerinde çalışan mikroservislerimize Application Insights ile dağıtık izleme (distributed tracing) nasıl bağlanır, ona bakacağız. Konu basit görünür ama 'checkout sayfası yavaş' raporu masaya geldiğinde tek tek loglara bakmakla işin içinden çıkamadığımı tecrübeyle söyleyebilirim. Hadi başlayalım.

Tipik bir akış şöyle: kullanıcı butona basıyor, istek API gateway'e geliyor, oradan order servisine, order payment'a, payment inventory'ye uzanıyor. Yavaşlık varsa kim suçlu? İşte trace context bunu söyleyen şey; her servisin loguna 'aynı isteğin parçasıyım' diyebilen bir kimlik koyuyor.

Application Insights nedir, neden Spring Apps ile?

Application Insights, Azure'ın APM (application performance monitoring) servisi. Java agent'ı sayesinde Spring Boot tarafında neredeyse hiç kod yazmadan SpringMVC çağrılarını, JDBC sorgularını, dış HTTP isteklerini ve log satırlarını otomatik enstrümante ediyor. Spring Apps tarafında ise platform-level bir entegrasyon var; uygulamayı yeniden paketlemenize gerek kalmadan agent'ı binding olarak ekliyor.

Bana sorarsanız buradaki en büyük kazanç, OpenTelemetry standardına oturmuş olması. Yarın provider'ı değiştirmek istesek, custom span'ları yeniden yazmıyoruz; sadece exporter değişiyor.

Resource'u oluşturup Spring Apps'e bağlamak

Önce bir Application Insights component'i açalım. --kind java demeyi unutmayın; portaldaki Java-spesifik panellerin gözükmesi buna bağlı:

az monitor app-insights component create \
  --app orders-insights \
  --location westeurope \
  --resource-group orders-rg \
  --kind java

az spring app-insights update \
  --name orders-spring \
  --resource-group orders-rg \
  --app-insights orders-insights \
  --sampling-rate 50

Sampling-rate konusunda küçük bir not: development'ta 100 mantıklı, her şeyi görmek istiyoruz. Production'da ise 25-50 arası bir değer hem maliyeti dizginliyor hem de istatistiksel olarak yeterli görünürlük sağlıyor. Şahsi tercihim, trafik düşük servisler için 100, yüksek olanlar için 25.

Logları trace ile korelasyonlamak

Burası çok atlanan bir nokta. Agent trace'i topluyor, logları topluyor, ama logback pattern'inize traceId ve spanId'yi koymadıysanız Kibana benzeri bir log aramasında trace'e geri dönemiyorsunuz. application.yml içinde:

logging:
  pattern:
    console: '%d{HH:mm:ss.SSS} [%thread] %-5level [traceId=%X{traceId} spanId=%X{spanId}] %logger{36} - %msg%n'

management:
  tracing:
    sampling:
      probability: 1.0

Bu kadarı bile End-to-End Transaction Details ekranında log satırını ilgili span'in altında görmek için yeterli.

Business operasyonları için custom span

Auto-instrumentation framework çağrılarını yakalar ama 'sipariş oluşturma' gibi domain operasyonlarınızı bilmez. Onları kendiniz isimlendirmelisiniz:

Span span = tracer.spanBuilder('createOrder')
    .setAttribute('order.customerId', customerId)
    .setAttribute('order.itemCount', items.size())
    .startSpan();

try (Scope scope = span.makeCurrent()) {
    return orderService.create(customerId, items);
} catch (Exception ex) {
    span.recordException(ex);
    span.setStatus(StatusCode.ERROR);
    throw ex;
} finally {
    span.end();
}

try/finally zorunlu; aksi halde exception yolunda span hiç kapanmaz ve Application Map'te uçuşan bir 'in-flight' kalır. Bunu ben de ilk başlarda iki üç kez unuttum.

Portalda ne göreceğiz?

Application Map size servis topolojisini çıkarıyor; ok kalınlıkları çağrı hacmini, kırmızılar hata oranını gösteriyor. Transaction Search zamana göre arama yapıyor. End-to-End Transaction Details ise bir isteğin waterfall görünümü; kim ne kadar bekletti, net olarak ortada. KQL ile de kendi sorgunuzu yazabilirsiniz, mesela en yavaş 20 endpoint:

requests
| where timestamp > ago(1h)
| top 20 by duration desc
| project name, duration, resultCode, operation_Id

Bağımlılıkların 95'inci yüzdelik gecikmesi:

dependencies
| where timestamp > ago(1d)
| summarize p95=percentile(duration, 95) by name
| order by p95 desc

Sık karşılaşılan tuzaklar

  • Connection string set edilmedi: Spring Apps binding'i yapılmamışsa agent telemetri gönderecek bir hedef bulamaz, sessiz sessiz dropluyor. Önce az spring app-insights show ile doğrulayın.
  • W3C trace context propagasyonu kopuk: Servislerden biri eski B3 header'ı kullanıyorsa trace iki parçaya bölünüyor. OpenTelemetry default'u W3C; tüm servislerin aynı standardı konuştuğundan emin olun.
  • Sampling-rate'i unutmak: Default genellikle düşük geliyor. Yeni resource'ta beklediğiniz trafiği göremezseniz ilk bakacağınız yer bu.
  • Custom span'i try/finally olmadan açmak: Exception olduğunda span kapanmaz, metric'ler çarpık çıkar.

Kapanış

Bu yazıda Azure Spring Apps üzerinde Application Insights ile dağıtık izlemenin temel kurulumunu, log korelasyonunu ve custom span üretimini gördük. Bence dağıtık izlemenin asıl değeri ilk incident'ta ortaya çıkıyor; o yüzden 'sonra kurarız' demek yerine projenin ilk haftasında devreye almak gerçekten zaman kazandırıyor. Umarım faydalı olur, bir sonraki yazıda görüşmek üzere.