---
title: 'Günlük Düzeyleri ve Yapılandırılmış Günlük'
source: 'https://academia.sh/tr/kurslar/java-ekosistem/gunluk-duzeyleri-ve-yapilandirilmis-gunluk'
course: 'Derleme, Test ve Ekosistem'
language: tr
updated: '2026-08-23T14:25:06+00:00'
license: 'CC BY-SA 4.0'
---

# Günlük Düzeyleri ve Yapılandırılmış Günlük

Aynı çağrı kümesinin iki ayrı eşikte kaç kayıt ürettiği koşturularak ölçülür ve eşiğin çağrı yerinde değil koşumda belirlendiği gösterilir; yapılandırılmış bir kaydın okunabilirliğinin alan sayısıyla değil aynı alanın her kayıtta bulunmasıyla ölçüldüğü örneklenir. Sınırlayıcı ölçüm, eşiğin altında kalan bir çağrının dizgi birleştirmeyle yazıldığında yine de değerlendirildiğini, Supplier ile yazıldığında değerlendirilmediğini gösterir.

Önceki ders bir adın yola nasıl girdiğini ölçtü: bildirimle, sırayla, ya da hiç girmeden.
Bu ders aynı çağrı kümesini sabit tutup değişen tek şeyi bir **eşik** yapıyor. Cephe bir
kayıt çağrısını gerçekleştirime taşıdı; şimdiki soru, o çağrının gerçekten bir kayda dönüşüp
dönüşmeyeceğini kimin belirlediği. Çağrı yerinde mi, yoksa programı çalıştıran tarafın
verdiği bir ayarda mı?

Günlük düzeyi ve yapılandırılmış günlük kavramları bu kursta yeni değil — **M22/K06
Gözlemlenebilirlik ve İşletim** ve **M19/K06 Başarım Karşıt Kalıpları ve İzleme** ikisini de
kavram olarak kurdu: düzeylerin sıralı bir ölçek olduğu, yapılandırılmış bir kaydın
serbest metinden farkı. Bu ders o kavramları tekrar tanımlamıyor; tek sorduğu şey, Java'nın
standart günlükleme kütüphanesinde bu iki kavramın **koşarken** nasıl davrandığı. Ölçüm
`java.util.logging` ile yapılıyor — üçüncü taraf bir günlükleme gerçekleştirimi değil,
dilin kendi standart kitaplığı. Önceki dersteki cephe/gerçekleştirim ayrımı burada da
geçerli: bir çağrı yerinin `gunlukcu.info(...)` yazması, o çağrının bir kayda dönüşeceğinin
garantisi değil. Aradaki fark, önceki derste "gerçekleştirim bulundu mu" sorusuna,
bu derste "düzey eşiği geçti mi" sorusuna bakıyor olması; ikisi de çağrı yerinin bilmediği,
koşum kurulumunun verdiği bir yanıt.

## Eşik Çağrı Yerinde Değil, Koşumda Belirleniyor

**GL9.** Bir günlük çağrısının işlenip işlenmeyeceği, çağrının taşıdığı düzeyin günlükçüye o
an atanmış eşikle karşılaştırılmasıyla belirlenir. **GL10.** Bu karşılaştırma koşum
zamanında olur; aynı derlenmiş çağrı kümesi, çalıştırma sırasında verilen eşiğe göre farklı
sayıda kayıt üretir.

```java
// EsikSayaci.java — ayni cagri kumesi iki ayri kosum esiginde kac kayit uretiyor
import java.util.logging.*;
import java.util.ArrayList;
import java.util.List;

public class EsikSayaci {
    static class SayanIsleyici extends Handler {
        List<String> yayimlanan = new ArrayList<>();
        public void publish(LogRecord r) { yayimlanan.add(r.getLevel() + " " + r.getMessage()); }
        public void flush() { }
        public void close() { }
    }

    static int calistir(Level esik) {
        Logger gunlukcu = Logger.getLogger("EsikDenemesi-" + esik);
        gunlukcu.setUseParentHandlers(false);
        SayanIsleyici h = new SayanIsleyici();
        h.setLevel(Level.ALL);
        gunlukcu.addHandler(h);
        gunlukcu.setLevel(esik);

        gunlukcu.fine("ayrinti");
        gunlukcu.info("ilerleme");
        gunlukcu.warning("uyari");
        gunlukcu.severe("kritik");

        return h.yayimlanan.size();
    }

    public static void main(String[] args) {
        int infoEsigindeUretilen = calistir(Level.INFO);
        int uyariEsigindeUretilen = calistir(Level.WARNING);

        System.out.println("dort cagri: fine, info, warning, severe");
        System.out.println("esik INFO iken uretilen kayit    : " + infoEsigindeUretilen);
        System.out.println("esik WARNING iken uretilen kayit : " + uyariEsigindeUretilen);
    }
}
```

```
dort cagri: fine, info, warning, severe
esik INFO iken uretilen kayit    : 3
esik WARNING iken uretilen kayit : 2
```

`calistir` yöntemi ikisinde de birebir aynı dört çağrıyı yapıyor: `fine`, `info`, `warning`,
`severe`. Değişen tek şey `gunlukcu.setLevel(esik)` satırına verilen değer. `INFO` eşiğinde
`fine` çağrısı süzülüyor, geri kalan üçü (`info`, `warning`, `severe`) işleyiciye ulaşıyor
(GL9). Eşik `WARNING`'e çekildiğinde `info` de süzülüyor, yalnız ikisi kalıyor. Bu iki koşum
aynı sınıf dosyasını kullanıyor — hiçbir yeniden derleme yok — ve yalnızca çalıştırma anında
verilen bir değerle farklı sayıda kayıt üretiyor (GL10). Çağrı yerindeki `gunlukcu.info(...)`
satırı ne INFO'nun geçeceğini ne geçmeyeceğini biliyor; bunu bilen taraf, günlükçüye eşiği
atayan koşum kurulumu.

**GL11.** Eşik tek bir ayar değildir: günlükçünün kendi eşiği ve ona bağlı her işleyicinin
kendi eşiği ayrı ayrı uygulanır; bir kaydın yayımlanması için ikisini de geçmesi gerekir.

```java
// IkiKatmanliEsik.java — gunlukcunun ve isleyicinin ayri esikleri var, kayit ikisini de gecmek zorunda
import java.util.logging.*;
import java.util.ArrayList;
import java.util.List;

public class IkiKatmanliEsik {
    static class SuzenIsleyici extends Handler {
        List<String> yayimlanan = new ArrayList<>();
        public void publish(LogRecord r) {
            if (!isLoggable(r)) return;
            yayimlanan.add(r.getLevel() + " " + r.getMessage());
        }
        public void flush() { }
        public void close() { }
    }

    static int calistir(Level gunlukcuEsigi, Level isleyiciEsigi) {
        Logger gunlukcu = Logger.getLogger("IkiKatman-" + gunlukcuEsigi + "-" + isleyiciEsigi);
        gunlukcu.setUseParentHandlers(false);
        SuzenIsleyici h = new SuzenIsleyici();
        h.setLevel(isleyiciEsigi);
        gunlukcu.addHandler(h);
        gunlukcu.setLevel(gunlukcuEsigi);

        gunlukcu.info("ilerleme");
        gunlukcu.warning("uyari");
        gunlukcu.severe("kritik");

        return h.yayimlanan.size();
    }

    public static void main(String[] args) {
        System.out.println("uc cagri: info, warning, severe");
        System.out.println("gunlukcu ALL, isleyici ALL     -> uretilen: " + calistir(Level.ALL, Level.ALL));
        System.out.println("gunlukcu ALL, isleyici WARNING -> uretilen: " + calistir(Level.ALL, Level.WARNING));
        System.out.println("gunlukcu WARNING, isleyici ALL -> uretilen: " + calistir(Level.WARNING, Level.ALL));
    }
}
```

```
uc cagri: info, warning, severe
gunlukcu ALL, isleyici ALL     -> uretilen: 3
gunlukcu ALL, isleyici WARNING -> uretilen: 2
gunlukcu WARNING, isleyici ALL -> uretilen: 2
```

Üç kurulumun üçü de aynı üç çağrıyı yapıyor. Günlükçü de işleyici de eşiği `ALL` iken
üçü de geçiyor. İkisinden yalnız biri `WARNING`'e çekildiğinde — fark etmez hangisi —
sonuç aynı: iki kayıt (GL11). Eşiği yalnız günlükçüde arayan bir okuma yanlış olurdu;
`SuzenIsleyici.publish` kendi `isLoggable` denetimini yapmasaydı, işleyicinin `setLevel`
çağrısı hiçbir şeyi süzmezdi ve son iki satır da üç kayıt verirdi. Eşiğin koşumda
belirlenmesi bu yüzden tek bir sayı değil, zincirdeki her halkanın kendi kararı. Bu, bir
günlükçüye birden fazla işleyici bağlandığında pratik bir sonuç doğuruyor: aynı kayıt kümesi
bir işleyicide görünüp ötekinde görünmeyebilir, çünkü her işleyici kendi eşiğini kendi
kararıyla uyguluyor ve günlükçünün eşiği yalnız ilk süzgeç.

## Yapılandırılmış Kayıt: Alan Sayısı Değil Alan Tutarlılığı

**GL12.** Yapılandırılmış bir kaydın alan adları çağrı yerinde rastgele seçilmez; üretici
tarafın uyduğu sabit bir bildirimde durur, ve okuyan taraf hangi alanı arayacağını bu
bildirime bakarak bilir. **GL13.** Bir günlük akışının okunabilirliği tek bir kaydın
taşıdığı alan **sayısıyla** değil, aynı alanın her kayıtta **bulunmasıyla** ölçülür.

Aşağıdaki ölçüm iki tasarımı karşılaştırıyor. Birincisinde her kayıt aynı iki alanı taşıyor:
`olay` ve `sayi`. İkincisinde çağrı yerleri kendi başlarına buyruk: bazıları fazladan bir
`kaynak` alanı ekliyor, bazıları `sayi` alanını hiç yazmıyor. Biçimlendirici ikisinde de aynı
— kayıt zaten çağrı yerinde tam metin olarak kuruluyor, biçimlendirici yalnız zaman alanı
eklemeden onu iletiyor (ortama bağlı veri yasağı burada da geçerli: zaman damgası hiç
basılmıyor).

```java
// AlanTutarliligi.java — okunabilirlik toplam alan sayisiyla degil, ayni alanin her kayitta bulunmasiyla olculuyor
import java.util.logging.*;
import java.util.ArrayList;
import java.util.List;

public class AlanTutarliligi {
    static class AlanBicimlendirici extends Formatter {
        public String format(LogRecord r) { return r.getMessage() + System.lineSeparator(); }
    }

    static class ToplayanIsleyici extends Handler {
        List<String> kayitlar = new ArrayList<>();
        Formatter b = new AlanBicimlendirici();
        public void publish(LogRecord r) { kayitlar.add(b.format(r)); }
        public void flush() { }
        public void close() { }
    }

    static boolean ikiAlanDaVar(String kayit) {
        return kayit.contains("olay=") && kayit.contains("sayi=");
    }

    static int toplamAlanSayisi(List<String> kayitlar) {
        int toplam = 0;
        for (String k : kayitlar) {
            for (String parca : k.trim().split(" ")) if (parca.contains("=")) toplam++;
        }
        return toplam;
    }

    public static void main(String[] args) {
        List<String> tutarli = new ArrayList<>();
        for (int i = 1; i <= 5; i++) tutarli.add("olay=siparis sayi=" + i);

        List<String> tutarsiz = List.of(
                "olay=siparis sayi=1 kaynak=web",
                "olay=siparis sayi=2 kaynak=web",
                "olay=siparis sayi=3 kaynak=mobil",
                "olay=siparis",
                "olay=siparis");

        long tutarliTam = tutarli.stream().filter(AlanTutarliligi::ikiAlanDaVar).count();
        long tutarsizTam = tutarsiz.stream().filter(AlanTutarliligi::ikiAlanDaVar).count();

        System.out.println("tutarli tasarim  - toplam alan: " + toplamAlanSayisi(tutarli)
                + ", iki alani da tasiyan kayit: " + tutarliTam + "/" + tutarli.size());
        System.out.println("tutarsiz tasarim - toplam alan: " + toplamAlanSayisi(tutarsiz)
                + ", iki alani da tasiyan kayit: " + tutarsizTam + "/" + tutarsiz.size());
    }
}
```

```
tutarli tasarim  - toplam alan: 10, iki alani da tasiyan kayit: 5/5
tutarsiz tasarim - toplam alan: 11, iki alani da tasiyan kayit: 3/5
```

`AlanBicimlendirici` ve `ToplayanIsleyici` bu ölçümde asıl işi yapmıyor — kaydı olduğu gibi
topluyorlar, çünkü asıl fark çağrı yerinde kuruluyor. Tutarsız tasarımın topladığı alan
**sayısı** tutarlıdan daha yüksek: 11'e karşı 10 (GL12'nin bildirime uyma zorunluluğu burada
yok, her çağrı yeri kendi kararını veriyor). Buna rağmen `sayi` alanını **her kayıtta**
arayan bir ayrıştırıcı tutarlı tasarımda beş kaydın beşini de okuyor, tutarsız tasarımda
yalnız üçünü (GL13). İki kayıt yalnızca `olay` taşıyor; bu kayıtlarda `sayi` yok, ve
ayrıştırıcı için "yok" ile "sıfır" aynı şey değil. Fazladan alan eklemek — üçüncü ve dördüncü
kayıtlardaki `kaynak` — ayrıştırılabilirliği artırmıyor; yalnız eksik kalan alan
azaltıyor. Bir günlük akışını **makineyle** okunabilir yapan şey, her kaydın aynı sabit
alan kümesini taşıması; hangi alanların bu kümede olduğu değil.

Bu ayrımın pratik sonucu, bir günlük akışını sonradan işleyen tarafın davranışında görünür.
Binlerce kayıt üzerinden `sayi` alanına göre bir toplam çıkarmak isteyen bir okuyucu,
tutarlı akışta beş kaydın beşini de güvenle işleyebilir; tutarsız akışta iki kaydı ya
atlamak ya da özel bir durum olarak ele almak zorunda kalır. Alan sayısının kayıttan kayda
değişmesi bir hata değildir — `AlanTutarliligi` örneğinde hiçbir istisna fırlamıyor,
hiçbir çağrı reddedilmiyor — ama okuyucunun her alanı **var sayamaması**, o akışı tek tek
elle okumaya zorlar. Yapılandırılmış günlüğün asıl kazancı burada: alan adlarını sabit bir
bildirime bağlamak, okuyucuyu bu varsayımdan kurtarıyor.

## Sınırlayıcı Ölçüm: Eşiğin Altındaki Çağrı Bedelsiz Değil

**GL14.** Eşiğin altında kalan bir çağrının ilettiği ifade, dizgi birleştirmeyle yazıldığında
düzeyden bağımsız olarak değerlendirilir. **GL15.** Aynı çağrı bir `Supplier` ile
yazıldığında ifade yalnız düzey etkinken değerlendirilir; eşiğin altında kalan çağrılarda
değerlendirme hiç çalışmaz.

Önceki iki ölçüm eşiğin **üretilen kayıt sayısını** belirlediğini gösterdi. Bundan "eşiğin
altında kalan çağrı bedelsizdir" sonucu çıkarılabilir gibi görünüyor, ama bu yanlış: eşik
yalnız kaydın **yayımlanıp yayımlanmayacağını** belirliyor, çağrıya verilen **argümanın
hesaplanıp hesaplanmayacağını** değil — hesaplama zamanı Java'nın yöntem çağırma kuralının
konusu, günlükleme çerçevesinin değil.

```java
// BedelliCagri.java — esigin altinda kalan cagrinin argumani dizgi birlestirmeyle her zaman, Supplier ile yalniz duzey acikken hesaplaniyor
import java.util.logging.*;

public class BedelliCagri {
    static int hesaplananIfade = 0;

    static String pahaliIfade() {
        hesaplananIfade++;
        return "deger=" + hesaplananIfade;
    }

    public static void main(String[] args) {
        Logger gunlukcu = Logger.getLogger("BedelDenemesi");
        gunlukcu.setUseParentHandlers(false);
        gunlukcu.addHandler(new Handler() {
            public void publish(LogRecord r) { }
            public void flush() { }
            public void close() { }
        });
        gunlukcu.setLevel(Level.WARNING);

        hesaplananIfade = 0;
        for (int i = 0; i < 5; i++) {
            gunlukcu.fine("ayrinti: " + pahaliIfade());
        }
        int istekliDegerlendirmeSayisi = hesaplananIfade;

        hesaplananIfade = 0;
        for (int i = 0; i < 5; i++) {
            gunlukcu.log(Level.FINE, () -> "ayrinti: " + pahaliIfade());
        }
        int tembelDegerlendirmeSayisi = hesaplananIfade;

        System.out.println("gunlukcunun esigi WARNING, bes cagrinin besi de FINE seviyesinde");
        System.out.println("dizgi birlestirmeyle (istekli) degerlendirilen ifade sayisi : " + istekliDegerlendirmeSayisi);
        System.out.println("Supplier ile (tembel) degerlendirilen ifade sayisi          : " + tembelDegerlendirmeSayisi);
    }
}
```

```
gunlukcunun esigi WARNING, bes cagrinin besi de FINE seviyesinde
dizgi birlestirmeyle (istekli) degerlendirilen ifade sayisi : 5
Supplier ile (tembel) degerlendirilen ifade sayisi          : 0
```

Günlükçünün eşiği `WARNING`; beş çağrının beşi de bundan daha düşük olan `FINE` düzeyinde ve
hiçbiri bir kayda dönüşmüyor. Buna rağmen `gunlukcu.fine("ayrinti: " + pahaliIfade())`
satırında `pahaliIfade()` beş kez de çalışıyor ve sayaç beşe çıkıyor (GL14): Java bir
yöntemi çağırmadan önce argümanlarını hesaplar, çağrının kendisi eşiğin altında kalacak olsa
bile bu sıra değişmiyor. İkinci döngü aynı beş çağrıyı `Logger.log(Level, Supplier<String>)`
biçimiyle yapıyor; bu aşırı yüklenmiş biçim, `Supplier`'ı yalnızca düzey etkinse çağırıyor —
eşiğin altında kalan beş çağrının beşinde de `get()` hiç işletilmiyor ve sayaç sıfırda
kalıyor (GL15). İki yazım da aynı dört düzeyi, aynı eşiği kullanıyor; fark yalnız argümanın
ne zaman hesaplandığında. Bu dersin ölçtüğü şey bir süre değil — hiçbir yerde bir zaman
ölçülmüyor — yalnız **değerlendirilen ifade sayısı**.

Bu fark Java'nın yöntem çağırma kuralından geliyor, günlükleme çerçevesinin bir eksikliği
değil: `gunlukcu.fine(mesaj)` çağrılmadan önce `mesaj` ifadesinin **değeri** hazır olmak
zorunda, çünkü Java bir çağrının argümanlarını çağrı gerçekleşmeden önce hesaplar — bu sıra
`fine` yönteminin içinde eşik denetimi yapıp yapmamasından bağımsız. `Supplier` bu sırayı
değiştirmiyor; yalnız hesaplamayı bir başka yöntemin — `get()`'in — arkasına saklıyor ve
o yöntemi çağırıp çağırmama kararını `fine` yönteminin içine, eşik denetiminden **sonraya**
taşıyor. Cephenin kendisi de aynı seçime açık: bir günlükleme cephesi çağırana yalnız
`Supplier` alan bir imza sunuyorsa, gerçekleştirim eşiği reddettiğinde ifade hiç
çalışmaz; cephe yalnız dizgi alan bir imza sunuyorsa, çağıran ne kadar dikkatli yazarsa
yazsın bu bedeli ödemekten kaçamaz. Bu, bir cephenin yalnız hangi yöntemleri **çağırmayı**
değil, hangi yöntemleri **sunmayı** seçtiğinin de ölçülebilir bir sonucu olduğunu gösteriyor.

## Özet

- Bir günlük çağrısının kayda dönüşüp dönüşmeyeceği, çağrının düzeyiyle günlükçüye atanmış
  eşiğin koşum zamanında karşılaştırılmasıyla belirlenir; aynı derlenmiş çağrı kümesi farklı
  eşiklerde farklı sayıda kayıt üretir.
- Yapılandırılmış bir kaydın alan adları üretici tarafın bildirimde uyduğu sabit bir kümedir;
  okuyan taraf bu kümeye bakarak hangi alanı arayacağını bilir.
- Bir günlük akışının okunabilirliği toplam alan sayısıyla değil, aynı alanın her kayıtta
  bulunmasıyla ölçülür; fazladan alan eklemek eksik kalan bir alanı telafi etmez.
- Sınırlayıcı ölçüm: eşiğin altında kalan bir çağrının argümanı dizgi birleştirmeyle
  yazıldığında yine de hesaplanır; `Supplier` ile yazıldığında yalnız düzey etkinken
  hesaplanır.
- Ölçülen şey bir süre değil, bir sayıdır: kaç kayıt üretildi, kaç alan tutarlıydı, kaç
  ifade hesaplandı.

## Sonraki Adım

Bu iki ders boyunca ölçülen her şey aynı çalışma zamanının içindeydi: aynı sanal makine,
aynı sınıf dosyası biçimi, tek bir kaynak dilden gelen kaynaklar. Sıradaki ders bu son
varsayımı kaldırıyor: koşum sınıf yoluna giren bir sınıf dosyası, kaynağı hiç Java
diliyle yazılmamış olsa bile aynı biçimde mi karşılanıyor, ve çağıran bunu fark ediyor mu?
