İçeriğe geç

Yapılandırılmış Loglama — Gece 2'de Doğru Satırı Bulmak

Orta 8 dk Sık karşılaşılır

Önce şunu oku: Actuator ve Gözlemlenebilirlik — Sağlık, Metrik, İz

30 saniyede özet

Bir hata çıktığında cevap loglardadır, ama onu bulup bulamayacağını logları nasıl yazdığın belirler. Alanlara ayrılmış JSON satırlar, her isteğin taşıdığı bir trace id ve maskelenmiş kişisel veri, aramayı tahminden kesinliğe taşır.

Gece 2’de telefonun çalıyor: bir müşterinin havalesi başarısız olmuş. Cevap bir yerlerde, üç servisin binlerce satırlık logunda yazılı. Onu beş dakikada mı, iki saatte mi bulacağın, logların nasıl yazıldığına bağlı.

  1. Bayt: Müşteri 42'nin havalesi başarısız olmuş. Loglarda '42' diye aradım.

  2. Sen: Ne çıktı?

  3. Bayt: Dokuz satır! Tutar 42 lira, sipariş 4211, port 8042... hangisi bizim müşteri?

  4. Bayt: Aramayı değil, yazmayı düzeltmek gerek. Logu bir veri gibi yazalım.

Logu veri gibi yazmak

Serbest metin log bir cümledir: “transfer failed for customer 42”. structured loggingLog satırlarını alanlardan oluşan kayıtlar (çoğunlukla JSON) olarak yazmak. Arama metin içinde değil, customerId gibi alanlarda yapılır.Sözlükte gör → ise alanlardan oluşur: level, customerId, transferId, msg. Arama metin içinde değil, alanlarda yapılır.

Spring Boot 3.4’ten beri bunun için bir ayar yeter: logging.structured.format.console: ecs. İsteğe özel alanlar ise MDCMapped Diagnostic Context: SLF4J/Logback'in thread'e bağlı anahtar-değer tablosu. Log pattern'i buradan okur; Micrometer Tracing traceId ve spanId'yi oraya koyar.Sözlükte gör → ile eklenir ve o isteğin bütün satırlarına kendiliğinden yazılır.

Kafam karıştı, daha basit anlat

Log bir cümle değil, etiketli bir kayıt olsun. Arama metinde değil, etikette yapılsın.

Hızlı kontrolBaşlangıç

Yapılandırılmış log nedir?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Serbest metin loglarda '42' diye arama yaptın. Neden alakasız satırlar da geldi?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Bir isteği izlemek

Havale isteği gateway, transfers ve ledger servislerinden geçti. Loglarda trace id yok. Saat 14:02:11'de dört farklı istek var. Ledger'daki hata satırı bu havaleye mi ait? Cevabı göster

Bilemezsin. Servisler arasında ortak bir kimlik olmadan satırları ancak zamana bakarak tahminle eşleştirirsin. Aynı saniyede dört istek varsa tahmin dörtte bir şanstır.

Etiket aramayı, ip izi kolaylaştırır.
Adım adım oku
  1. Karalanmış notlu kutular: '42' her yerde geçiyor.
  2. Etiketli kutular: yalnızca 'müşteri: 42' olanlar seçiliyor.
  3. Renkli ip: bir isteğin üç servisteki kutuları birbirine bağlı.
  4. Etiket ve ip birlikte: kök neden tek zincirde.

Trace id bir isteğin kimliğidir: istek gateway’e girdiği an üretilir ve her servise traceparent başlığıyla taşınır. Spring Boot’ta Micrometer Tracing onu her log satırına kendiliğinden ekler.

Kafam karıştı, daha basit anlat

Trace id, bir isteğin bütün servislerdeki satırlarını bağlayan iptir. Onunla bir isteği baştan sona izlersin.

Hızlı kontrolOrta

Trace id ne işe yarar?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Trace id olmadan üç servisin logunda bir isteği izlemeye çalışıyorsun. Ne yapmak zorunda kalırsın?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Kendin gör

Müşteri 42'nin havalesi neden başarısız?

Tohum 94664

Bir log satırı böyle yazılıyor:

14:02:11 ERROR transfer failed for customer 42 iban TR330006100519786457841326

Oynat ya da adımla: her adım aramanın bir aşaması.

Hız
Adım 0

Şu an ne oldu?

Saat 14:02, bir havale başarısız

Cevap üç servisin loglarında. Onu ne kadar hızlı bulacağını, logları nasıl yazdığın belirliyor.

Görevler0/3

  • Aramada alakasız satırlara boğulaçık

    İpucu

    Varsayılan ayarlar yeter.

  • Kök nedeni kesin olarak bulaçık

    İpucu

    İki alışkanlık birlikte gerekiyor.

  • Kök nedeni bul ve log'da hiçbir açık IBAN bırakmaaçık

    İpucu

    Üç anahtar.

Olay günlüğü (0)

Henüz olay yok. Oynat veya adımla.

  1. Varsayılanla oynat. Dokuz eşleşme, kök neden belirsiz, IBAN açık.
  2. Yapılandırılmış log’u aç. Üç satır, üçü de bu müşterinin; ama servisler arası bağ yok.
  3. Trace id’yi aç. Kök neden bulundu: ledger timeout.
  4. Kişisel veriyi maskele. Aynı sonuç, ama log’da IBAN maskeli.
Hızlı kontrolOrta

MDC'ye bir alan koymak ne sağlar?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

MDC'ye müşteri numarası konuyor ama istek bitince temizlenmiyor. Thread havuzunda ne olur?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Tuzaklar

Log’da kişisel veri. IBAN, kart numarası ya da kimlik numarası log’a açık yazılırsa, loglara erişen herkes onları görür. Maskelemeyi tek bir yerde, değer nesnesinin kendisinde yap.

Her şey DEBUG. Canlıda bütün paketleri DEBUG seviyesinde loglamak, sinyali gürültüye gömer ve depolama maliyetini katlar. Varsayılan INFO, hata ayıklarken yalnızca ilgili paket DEBUG.

Hatayı yutmak. log.error("hata") deyip istisnanın kendisini yazmamak, yığın izini kaybettirir. İstisnayı son parametre olarak ver.

Kafam karıştı, daha basit anlat

Kişisel veriyi maskele, seviyeyi bilinçli seç, istisnayı satıra ekle.

Hızlı kontrolOrta

Log'a IBAN'ın tamamını yazmanın riski nedir?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Aşağıdaki örnek bir bankanın havale servisinden: JSON log, istek boyunca taşınan müşteri numarası, kendini maskeleyen IBAN.

Derinleş · Havale servisi: aranabilir, izlenebilir, güvenli log 4 dosya · ~66 satır · ilk okumada atlayabilirsin
Proje dosyaları

src/main/resources/ application.yml Ayar: konsola ECS biçiminde JSON log; seviyeler bilinçli.

src/main/resources/application.yml
logging:
structured:
format:
console: ecs # one JSON object per line (Spring Boot 3.4+)
level:
root: INFO
com.bank.transfer: INFO
management:
tracing:
sampling:
probability: 1.0 # traceId and spanId are added to every log line

src/main/java/com/bank/web/ CustomerLogContext.java Interceptor: oturum açmış müşterinin numarasını MDC ile her satıra ekliyor, istek bitince temizliyor.

src/main/java/com/bank/web/CustomerLogContext.java
// Adds the authenticated customer to every log line of this request, and
// removes it afterwards so a pooled thread does not carry it into the next one.
// The id comes from the principal Spring Security set, never from a client header.
@Component
class CustomerLogContext implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
Principal principal = request.getUserPrincipal();
if (principal != null) MDC.put("customerId", principal.getName());
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
MDC.remove("customerId");
}
}

src/main/java/com/bank/transfer/ Iban.java IBAN değer nesnesi: toString her zaman maskeli, log'a açık yazılamıyor.

src/main/java/com/bank/transfer/Iban.java
// Whatever logs an Iban gets the masked form; the full value needs an explicit call.
public record Iban(String value) {
public Iban {
Objects.requireNonNull(value);
}
public String full() {
return value;
}
@Override
public String toString() {
return value.substring(0, 4) + "****" + value.substring(value.length() - 4);
}
}

src/main/java/com/bank/transfer/ TransferService.java Servis: istisna son parametre olarak veriliyor, yığın izi kaybolmuyor.

src/main/java/com/bank/transfer/TransferService.java
@Service
class TransferService {
private static final Logger log = LoggerFactory.getLogger(TransferService.class);
private final Ledger ledger;
TransferService(Ledger ledger) {
this.ledger = ledger;
}
TransferResult send(TransferRequest request) {
try {
return ledger.post(request);
} catch (LedgerTimeoutException ex) {
// The exception goes last: the stack trace is kept. The IBAN prints masked.
log.error("transfer failed transferId={} to={}", request.id(), request.to(), ex);
throw new TransferFailedException(request.id(), ex);
}
}
}

Kendini sına

Şimşek turu1/4

Serbest metin log'da müşteri numarasıyla arama, yalnızca o müşterinin satırlarını getirir.

Soru 1/3İleri

log.error("transfer failed") yazıldı ama istisna parametre olarak verilmedi. Sonuç?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Aklında kalacak üç şey

  1. 1 Serbest metin log'da arama, aradığın sayının geçtiği her satırı getirir. Alanlara ayrılmış JSON log'da müşteri numarasına göre süzersin ve yalnızca o müşterinin satırları gelir.
  2. 2 Bir isteğin bütün satırları servisler boyunca aynı trace id'yi taşırsa, isteği uçtan uca izlersin. Trace id yoksa satırları zamana bakarak tahminle eşleştirirsin.
  3. 3 IBAN ve kart numarası gibi kişisel veriler log'a maskelenerek yazılır. Loglar geniş bir ekibe açıktır ve uzun süre saklanır.
Sonraki kapı Uygulama şifreni hiç görmeden seni nasıl tanıyor? Ve biri dönüş kodunu çalarsa ne olur? OAuth2 ve OIDC — Şifreni Vermeden Nasıl Giriş Yapılır? · 9 dk

4 kart sonraki derste seni bekliyor

0/4 kart bu dersten toplandı