---
title: 'Günlükleme Kurulumu'
source: 'https://academia.sh/tr/kurslar/sunucu-temelleri/gunlukleme-kurulumu'
course: 'Sunucu Tarafı Temelleri'
language: tr
updated: '2026-08-17T18:06:52+00:00'
license: 'CC BY-SA 4.0'
---

# Günlükleme Kurulumu

Kütük satırları JSON nesnesi olarak üretilir; istek bağlamı eşzamansız yerel depoyla taşınır, derin katmanlar istek kimliğini parametre almadan kaydeder, düzey eşiği satır sayısını belirler ve tek bir isteğin bütün satırları sorguyla toplanır.

Önceki dersin kütük satırları JSON biçimindeydi ve her satır bir istek kimliği taşıyordu, ama
o kimlik yalnız iki yerde elle yazıldı. Gerçek bir uygulamada aynı istek zincirin halkalarından,
işleyiciden, alan katmanından ve veri katmanından geçer; her katman kendi kaydını üretir.
Kimliğin her satırda bulunması için her fonksiyonun onu parametre olarak alması gerekir mi?

Bu ders üç şeyi kurar: kütük satırının biçimi, istek bağlamının katmanlar arasında taşınması
ve satırların sonradan sorgulanması. Ölçüm, eşzamanlı üç isteğin kayıtlarının birbirine
karışmadığını gösterecek.

## Satır Neden Nesnedir

Serbest metin bir kütük satırı insan tarafından okunur, makine tarafından okunmaz. "Ödünç
isteği reddedildi, ISBN 978-0262033848" satırından ISBN'i çıkarmak için bir kalıp yazmak
gerekir ve kalıp, ileti her değiştiğinde bozulur.

**Yapılandırılmış kayıt** (structured log record) bu sorunu adlandırılmış alanlarla çözer.
Her satır bağımsız bir JSON nesnesidir; satırlar arasında ayraç yoktur, dosya satır satır
işlenir. Bir kaydın taşıması gereken alanlar dört öbekte toplanır:

- **Ne zaman**: ISO 8601 biçiminde bir zaman damgası. Sabit uzunlukta, sıralanabilir ve saat
  dilimi taşır.
- **Ne kadar önemli**: düzey. Kaydın eşiğe göre yazılıp yazılmayacağını belirler.
- **Ne oldu**: olay adı. Serbest cümle değil, sabit bir tanımlayıcı — çünkü sorgulanacaktır.
- **Hangi bağlamda**: istek kimliği ve olaya özgü alanlar.

## Bağlamı Taşımak

İstek kimliğini her fonksiyona parametre olarak vermek işe yarar ama bedeli yüksektir: kaydın
kendisiyle ilgisi olmayan fonksiyonların imzaları değişir ve zincirin ortasındaki bir
fonksiyon parametreyi iletmeyi unuttuğunda bağ kopar.

Çalışma zamanı bunun için **eşzamansız yerel depo** (async local storage) sağlar. Bir değer
belirli bir çağrı ağacına bağlanır; o ağacın içinden yapılan her okuma — kaç `await` sonrası
olursa olsun — aynı değeri görür. Eşzamanlı isteklerin ağaçları ayrı olduğu için değerler
karışmaz.

```js
// kaynak/kurulum/kutuk.mjs — JSON satirlari uretir; istek baglamini es zamansiz depodan okur
import { AsyncLocalStorage } from "node:async_hooks";

const DUZEYLER = { hata: 0, uyari: 1, bilgi: 2, ayrintili: 3 };

export const istekDeposu = new AsyncLocalStorage();

export const kutukKur = (esikAdi) => {
  const esik = DUZEYLER[esikAdi] ?? DUZEYLER.bilgi;
  const yaz = (duzey, olay, alanlar = {}) => {
    if (DUZEYLER[duzey] > esik) return;                  // duzey esigi: altindakiler yazilmaz
    const baglam = istekDeposu.getStore() ?? {};         // istek kimligi elden gecirilmez
    console.log(JSON.stringify({
      an: new Date().toISOString(), duzey, olay, ...baglam, ...alanlar,
    }));
  };
  return Object.fromEntries(
    Object.keys(DUZEYLER).map((d) => [d, (olay, alanlar) => yaz(d, olay, alanlar)]));
};
```

Düzeyler sayısal karşılıklarıyla tanımlanmıştır; karşılaştırma bu sayılar üzerinden yapılır.
Eşik `bilgi` olduğunda `ayrintili` kayıtlar hiç yazılmaz — dosyaya yazılıp sonra süzülmezler,
üretilmezler bile. Bu ayrım maliyettedir: eşiğin altındaki bir kayıt için dizileştirme
yapılmaz.

Sunucu her isteği kendi bağlamında çalıştırır. Veri katmanındaki `rafKontrol` fonksiyonu
istek kimliğini almaz, bilmez ve iletmez; buna rağmen ürettiği satırlar kimliği taşır.

```js
// kaynak/http/sunucu.mjs — her istek kendi baglaminda calisir; derin katmanlar kimligi tasimaz
import { createServer } from "node:http";
import { setTimeout as bekle } from "node:timers/promises";
import { kutukKur, istekDeposu } from "../kurulum/kutuk.mjs";

const kutuk = kutukKur(process.env.KUTUPHANE_KUTUK_DUZEYI ?? "bilgi");
const RAFTAKI = new Set(["978-0262033848"]);
let sayac = 0;

// Veri katmanindaki derin fonksiyon: istek kimligini parametre olarak ALMAZ.
const rafKontrol = async (isbn) => {
  kutuk.ayrintili("raf_sorgusu", { isbn });
  await bekle(150);                                   // depo erisimini taklit eden gecikme
  const bulundu = RAFTAKI.has(isbn);
  kutuk.bilgi("raf_sonucu", { isbn, bulundu });
  return bulundu;
};

createServer((istek, yanit) => {
  yanit.sendDate = false;
  const baglam = { istekKimligi: `i-${++sayac}` };
  istekDeposu.run(baglam, async () => {               // baglam bu istegin butun zincirinde
    const baslangic = process.hrtime.bigint();
    const adres = new URL(istek.url, "http://yerel");
    kutuk.bilgi("istek_alindi", { yontem: istek.method, yol: adres.pathname });

    let durum = 404, govde = { hata: "yol_yok" };
    if (adres.pathname === "/odunc") {
      const bulundu = await rafKontrol(adres.searchParams.get("isbn") ?? "");
      durum = bulundu ? 201 : 409;
      govde = bulundu ? { odunc: "verildi" } : { hata: "raf_bos" };
      if (!bulundu) kutuk.uyari("odunc_reddedildi", { neden: "raf_bos" });
    } else if (adres.pathname === "/kitaplar") {
      durum = 200;
      govde = { kitapSayisi: RAFTAKI.size };
    }

    const metin = JSON.stringify({ ...govde, istekKimligi: baglam.istekKimligi });
    yanit.writeHead(durum, { "content-type": "application/json; charset=utf-8",
      "content-length": Buffer.byteLength(metin), "x-istek-kimligi": baglam.istekKimligi });
    yanit.end(metin);

    const sure = Number(process.hrtime.bigint() - baslangic) / 1e6;
    kutuk.bilgi("istek_bitti", { durum, sureMs: Number(sure.toFixed(1)) });
  });
}).listen(8438, "127.0.0.1", () => kutuk.bilgi("sunucu_acildi", { baglantiNoktasi: 8438 }));
```

İstek kimliği yanıtta da bulunur: hem `x-istek-kimligi` başlığında hem gövdede. Bir üye
destek istediğinde verdiği numara doğrudan kütüğe götürür.

## Ölçüm

Ölçüm sunucuyu iki farklı eşikle çalıştırır. Her turda üç istek eşzamanlıya yakın gönderilir:
rafta bulunan bir kitap için ödünç, hızlı bir katalog isteği ve rafta bulunmayan bir kitap için
ödünç. Süzme işi `jq` ile yapılır; bu, JSON akışı üzerinde çalışan bir süzgeçtir.

```bash
#!/usr/bin/env bash
# Sunucuyu iki duzey esigiyle acar; es zamanli uc istegin kutuk satirlarini toplar ve sorgular.
tur() {  # $1 = kutuk duzeyi, $2 = cikti dosyasi
  KUTUPHANE_KUTUK_DUZEYI="$1" node kaynak/http/sunucu.mjs > "$2" 2>&1 & s=$!
  sleep 0.7
  curl -sS -o /dev/null 'http://127.0.0.1:8438/odunc?isbn=978-0262033848' & p1=$!
  sleep 0.05
  curl -sS -o /dev/null 'http://127.0.0.1:8438/kitaplar' & p2=$!
  sleep 0.05
  curl -sS -o /dev/null 'http://127.0.0.1:8438/odunc?isbn=978-0000000000' & p3=$!
  wait "$p1" "$p2" "$p3"
  sleep 0.2
  kill "$s"; wait "$s" 2>/dev/null
}

tur bilgi kutuk-bilgi.jsonl
tur ayrintili kutuk-ayrintili.jsonl

echo "--- kutuk (esik=bilgi), zaman sirasinda ---"
cat kutuk-bilgi.jsonl

echo "--- tek bir istegin butun satirlari: i-1 ---"
jq -c 'select(.istekKimligi == "i-1")' kutuk-bilgi.jsonl

echo "--- duzeye gore satir sayisi ---"
for dosya in kutuk-bilgi.jsonl kutuk-ayrintili.jsonl; do
  printf '%-24s %s\n' "$dosya" \
    "$(jq -r .duzey "$dosya" | sort | uniq -c | tr '\n' ' ' | tr -s ' ')"
done

rm -f kutuk-bilgi.jsonl kutuk-ayrintili.jsonl
```

```
--- kutuk (esik=bilgi), zaman sirasinda ---
{"an":"2026-07-28T20:26:21.198Z","duzey":"bilgi","olay":"sunucu_acildi","baglantiNoktasi":8438}
{"an":"2026-07-28T20:26:21.897Z","duzey":"bilgi","olay":"istek_alindi","istekKimligi":"i-1","yontem":"GET","yol":"/odunc"}
{"an":"2026-07-28T20:26:21.954Z","duzey":"bilgi","olay":"istek_alindi","istekKimligi":"i-2","yontem":"GET","yol":"/kitaplar"}
{"an":"2026-07-28T20:26:21.955Z","duzey":"bilgi","olay":"istek_bitti","istekKimligi":"i-2","durum":200,"sureMs":1}
{"an":"2026-07-28T20:26:22.016Z","duzey":"bilgi","olay":"istek_alindi","istekKimligi":"i-3","yontem":"GET","yol":"/odunc"}
{"an":"2026-07-28T20:26:22.049Z","duzey":"bilgi","olay":"raf_sonucu","istekKimligi":"i-1","isbn":"978-0262033848","bulundu":true}
{"an":"2026-07-28T20:26:22.050Z","duzey":"bilgi","olay":"istek_bitti","istekKimligi":"i-1","durum":201,"sureMs":153.5}
{"an":"2026-07-28T20:26:22.166Z","duzey":"bilgi","olay":"raf_sonucu","istekKimligi":"i-3","isbn":"978-0000000000","bulundu":false}
{"an":"2026-07-28T20:26:22.167Z","duzey":"uyari","olay":"odunc_reddedildi","istekKimligi":"i-3","neden":"raf_bos"}
{"an":"2026-07-28T20:26:22.168Z","duzey":"bilgi","olay":"istek_bitti","istekKimligi":"i-3","durum":409,"sureMs":152.3}
--- tek bir istegin butun satirlari: i-1 ---
{"an":"2026-07-28T20:26:21.897Z","duzey":"bilgi","olay":"istek_alindi","istekKimligi":"i-1","yontem":"GET","yol":"/odunc"}
{"an":"2026-07-28T20:26:22.049Z","duzey":"bilgi","olay":"raf_sonucu","istekKimligi":"i-1","isbn":"978-0262033848","bulundu":true}
{"an":"2026-07-28T20:26:22.050Z","duzey":"bilgi","olay":"istek_bitti","istekKimligi":"i-1","durum":201,"sureMs":153.5}
--- duzeye gore satir sayisi ---
kutuk-bilgi.jsonl         9 bilgi 1 uyari
kutuk-ayrintili.jsonl     2 ayrintili 9 bilgi 1 uyari
```

Zaman damgaları ve süre alanları her çalıştırmada değişir; satırların birbirine göre sırası
da zamanlamaya bağlı olarak kayabilir.

## Ölçümün Söyledikleri

**Satırlar iç içe geçmiştir.** İlk isteğin `istek_alindi` satırı ile `raf_sonucu` satırı
arasında ikinci ve üçüncü isteğe ait dört satır vardır. Zaman sırasında okunan bir kütükte
tek bir isteğin akışı görünmez; bu, tek süreçte eşzamanlı çalışan bir uygulamanın kaçınılmaz
sonucudur.

**Kimlikler karışmamıştır.** `rafKontrol` fonksiyonu üç istekten ikisi tarafından çağrılmış,
her çağrı yüz elli milisaniye beklemiş ve bu süre boyunca başka isteklerin kodu çalışmıştır.
Buna rağmen `raf_sonucu` satırlarının biri `i-1`, diğeri `i-3` kimliğini taşır. Fonksiyon bu
kimliği hiçbir parametreden almamıştır.

**Sorgu akışı geri kurar.** `jq` süzgeci tek bir kimliğe ait satırları seçtiğinde ortaya
isteğin kendi öyküsü çıkar: alındı, raf sorgulandı, bitti. Yapılandırılmış kaydın asıl kazancı
budur — kütük zaman sırasında yazılır, ihtiyaca göre yeniden düzenlenir.

**Süre ölçümü kaydın parçasıdır.** `istek_bitti` satırındaki `sureMs` alanı, ikinci isteğin
bir milisaniyede, ödünç isteklerinin yüz elli milisaniyenin üzerinde tamamlandığını gösterir.
Aynı alan bütün satırlarda aynı adı taşıdığı için yavaş isteklerin sorgulanması tek bir
süzgeç ifadesidir.

**Eşik satır sayısını belirler.** Eşik `bilgi` iken dosyada on satır vardır; `ayrintili`
olduğunda `raf_sorgusu` kayıtları eklenir ve sayı on ikiye çıkar. Eşiğin yapılandırmadan
gelmesi, aynı sürümü yeniden kurmadan ayrıntı düzeyini değiştirmeyi sağlar.

## Kütük Nereye Yazılır

Sunucu kütüğü standart çıktıya yazar; dosya açmaz, döndürmez, sıkıştırmaz. Bu bir eksiklik
değil, bilinçli bir sınırdır: uygulamanın işi kaydı **üretmektir**, saklamak değil.

Gerekçesi önceki bölümde ölçülmüştü. Uygulama birden çok süreç olarak çalışır ve süreçler
ölümlüdür; her sürecin kendi dosyasına yazması, aynı isteğin izini birden çok dosyaya dağıtır
ve ölen sürecin dosyası sahipsiz kalır. Standart çıktıya yazıldığında toplama işini süreci
çalıştıran katman üstlenir: bütün süreçlerin çıktısı tek bir akışta birleşir.

Bu ayrımın adı ve gerekçesi bu bölümün son dersinde, taşınabilirlik ilkeleri arasında yeniden
ele alınacak.

## Özet

- Yapılandırılmış kayıt her satırı bağımsız bir JSON nesnesi olarak yazar; zaman damgası
  ISO 8601 biçimindedir, olay adı sabit bir tanımlayıcıdır ve bağlam adlandırılmış alanlarda
  taşınır.
- Düzey eşiği karşılaştırması sayısaldır ve eşiğin altındaki kayıt hiç üretilmez; ölçümde eşik
  `bilgi` iken on, `ayrintili` iken on iki satır yazılmıştır.
- Eşzamansız yerel depo istek bağlamını çağrı ağacına bağlar; veri katmanındaki fonksiyon
  istek kimliğini parametre almadığı hâlde ürettiği satırlar doğru kimliği taşımıştır.
- Eşzamanlı isteklerin satırları zaman sırasında iç içe geçer; tek bir isteğin akışı ancak
  kimliğe göre süzülerek geri kurulur.
- Kütük standart çıktıya yazılır; toplama ve saklama işi süreci çalıştıran katmana bırakılır,
  çünkü süreçler çok sayıda ve ölümlüdür.

## Sonraki Adım

Bu dersin sunucusunda hata durumu tek bir dala sıkışmıştı: rafta bulunmayan kitap `409`
üretiyordu. Gerçek bir uygulamada hatalar iki ayrı kaynaktan gelir — kullanıcının yanlış
isteği ve uygulamanın kendi kusuru — ve ikisi aynı biçimde ele alınamaz. Sonraki ders ortak
hata yanıtı üretimini kurar: hangi hata hangi durum koduna eşlenir, yanıt gövdesi hangi
alanları taşır ve gövdenin iç ayrıntı sızdırmadığı hangi sınamayla doğrulanır?
