---
title: Günlükleme
source: 'https://academia.sh/tr/kurslar/nodejs/gunlukleme'
course: 'Node.js Çalışma Zamanı'
language: tr
updated: '2026-08-17T18:09:51+00:00'
license: 'CC BY-SA 4.0'
---

# Günlükleme

Serbest metin yerine yapılandırılmış kayıt, düzey eşiği, isteğe bağlı alanların taşınması, kütüğün standart çıktıya yazılması ve kayıtların araçlarla incelenmesi.

Hata Yönetimi Stratejisi dersinde arızaların kaydedilmesinden birkaç kez söz edildi
ama nereye ve hangi biçimde yazılacağı belirlenmedi. Serbest metin bir satır —
`hata: dosya acilamadi` — arızayı arayan kişiye yetmez: hangi dosya, hangi istek,
hangi süreç, kaç kez?

Bu ders kütük biçimini belirler. Terim seçimi şudur: **kütük (log)**, sürecin
ürettiği kayıt dizisidir; **günlükleme (logging)** bu kayıtları üretme işidir.

## Yapılandırılmış Kayıt

Kütük satırı iki okuyucuya birden hitap eder: arıza sırasında bakan insan ve
kayıtları süzen, sayan, uyarı üreten program. İkisini birden karşılayan biçim, her
satırın kendi başına ayrıştırılabilir bir kayıt olmasıdır.

Ölçüm dosyasının biçimi burada da işe yarar: satır başına bir JSON nesnesi. Kabuk
Programlama kursundaki satır tabanlı araçlar bu biçimle doğrudan çalışır.

```js
// kutuk.mjs
const DUZEYLER = { ayrinti: 20, bilgi: 30, uyari: 40, hata: 50 };

export function kutukOlustur({
  duzey = 'bilgi',
  hizmet,
  cikti = process.stdout,
  saat = () => new Date().toISOString(),   // sinamada sabitlenebilsin
  sabitAlanlar = {},
} = {}) {
  const esik = DUZEYLER[duzey] ?? DUZEYLER.bilgi;

  function yaz(duzeyAdi, ileti, alanlar = {}) {
    if (DUZEYLER[duzeyAdi] < esik) return;
    cikti.write(JSON.stringify({
      zaman: saat(),
      duzey: duzeyAdi,
      hizmet,
      ileti,
      ...sabitAlanlar,
      ...alanlar,
    }) + '\n');
  }

  const kutuk = {};
  for (const ad of Object.keys(DUZEYLER)) {
    kutuk[ad] = (ileti, alanlar) => yaz(ad, ileti, alanlar);
  }
  // Her isteğe kendi alanlarını taşıyan bir alt kütük
  kutuk.alt = (ekAlanlar) => kutukOlustur({
    duzey, hizmet, cikti, saat,
    sabitAlanlar: { ...sabitAlanlar, ...ekAlanlar },
  });
  return kutuk;
}
```

```js
// kutuk-dene.mjs
import { kutukOlustur } from './kutuk.mjs';

const kutuk = kutukOlustur({
  duzey: process.env.OLCUM_KUTUK_DUZEYI ?? 'bilgi',
  hizmet: 'olcum-toplayici',
  saat: () => '2024-02-07T09:12:44.000Z',      // ornek ciktisi degismesin diye sabit
});

kutuk.ayrinti('yapilandirma okundu', { dosya: '.env' });
kutuk.bilgi('istek alindi', { yol: '/olcum', yontem: 'POST', kayit: 12 });
kutuk.uyari('esik asildi', { dugum: 'kenar-03', deger: 24.6 });
kutuk.hata('olcum dosyasi acilamadi', { kod: 'ENOENT', yol: 'yok.ndjson' });

const istekKutugu = kutuk.alt({ istekKimligi: 'r-0007' });
istekKutugu.bilgi('yanit yazildi', { durum: 201, sure_ms: 3 });
```

```sh
node kutuk-dene.mjs
```

```
{"zaman":"2024-02-07T09:12:44.000Z","duzey":"bilgi","hizmet":"olcum-toplayici","ileti":"istek alindi","yol":"/olcum","yontem":"POST","kayit":12}
{"zaman":"2024-02-07T09:12:44.000Z","duzey":"uyari","hizmet":"olcum-toplayici","ileti":"esik asildi","dugum":"kenar-03","deger":24.6}
{"zaman":"2024-02-07T09:12:44.000Z","duzey":"hata","hizmet":"olcum-toplayici","ileti":"olcum dosyasi acilamadi","kod":"ENOENT","yol":"yok.ndjson"}
{"zaman":"2024-02-07T09:12:44.000Z","duzey":"bilgi","hizmet":"olcum-toplayici","ileti":"yanit yazildi","istekKimligi":"r-0007","durum":201,"sure_ms":3}
```

Zaman damgası örnekte sabitlendi; gerçek çalıştırmada her satır kendi anını taşır.
Saatin dışarıdan verilebilmesi bir kolaylık değil, sınanabilirlik gereğidir: Test
dersinde bu kanca kullanılacak.

İleti metni sabittir, değişken bilgi ayrı alanlara konur. Bu ayrım aramayı olanaklı
kılar: `"ileti":"istek alindi"` araması bütün istekleri bulur, dosya adı iletiye
gömülseydi her satır farklı olurdu.

## Düzey Eşiği

Dört düzey sayısal ağırlıklarla sıralanır ve eşiğin altındaki kayıtlar hiç
yazılmaz. Eşik yapılandırmadan gelir, yani kütük hacmi kod değiştirmeden ayarlanır.

```sh
OLCUM_KUTUK_DUZEYI=hata node kutuk-dene.mjs
```

```
{"zaman":"2024-02-07T09:12:44.000Z","duzey":"hata","hizmet":"olcum-toplayici","ileti":"olcum dosyasi acilamadi","kod":"ENOENT","yol":"yok.ndjson"}
```

Düzeylerin anlamı sözleşmeyle sabitlenir; anlamsız bir sıra, eşiği işe yaramaz kılar:

- **hata** — işlem tamamlanamadı, birinin bakması gerekiyor.
- **uyari** — işlem tamamlandı ama beklenmeyen bir durum var.
- **bilgi** — hizmetin olağan akışındaki kilometre taşları.
- **ayrinti** — tanı için gereken ayrıntı; olağan çalışmada kapalıdır.

En sık yapılan yanlış, her şeyi `hata` düzeyine yazmaktır. Gerçek arıza, gürültünün
içinde görünmez hâle gelir ve düzey ayrımı anlamını yitirir.

## İsteğe Bağlı Alanları Taşımak

Eşzamanlı çalışan bir hizmette kütük satırları karışık gelir; bir isteğe ait
satırları toplayabilmek için ortak bir alan gerekir. `alt` yöntemi, verilen alanları
kendisinden üretilen bütün satırlara ekleyen bir kütük döndürür.

Yukarıdaki çıktının son satırında `istekKimligi` alanı bu yolla eklendi. İstek
başında bir kimlik üretilip alt kütük oluşturulduğunda, o isteğin bütün kayıtları
aynı değeri taşır ve tek bir aramayla toplanır.

Kimliğin istemciden gelen bir başlıkta taşınması, isteği birden çok hizmet üzerinden
izlemeyi olanaklı kılar. Gelen kimliğe doğrudan güvenilmez: biçimi doğrulanır ya da
üretilen kendi kimliğiyle birlikte iki alan olarak yazılır.

## Hatayı Kütüğe Yazmak

Kütüğe yazılan en önemli kayıt hata kaydıdır ve en sık yanlış yazılan da odur. Hata
nesnesi doğrudan bir alana konursa iki sorun çıkar. Birincisi, `Error` nesnesinin
`name`, `message` ve `stack` alanları sayılabilir değildir; dizileştirme onları
atlar ve geriye boş bir nesne kalır. İkincisi, nesnenin tamamını yaymak
(`...hata`) Yapılandırma Yönetimi dersindeki maskelemeyi geçersiz kılar — bir
bağlantı hatası, bağlantı dizgisini kendi alanında taşıyor olabilir.

Doğru yol, yazılacak alanları açıkça seçmektir:

```js
// kutuk-hata.mjs
export function hataAlanlari(hata, { yigitIzi = false } = {}) {
  const alan = { hataAdi: hata.name, hataIletisi: hata.message };
  if (hata.kod !== undefined) alan.hataKodu = hata.kod;
  else if (hata.code !== undefined) alan.hataKodu = hata.code;

  const zincir = [];
  let neden = hata.cause;
  while (neden instanceof Error && zincir.length < 5) {
    zincir.push(neden.code ?? neden.kod ?? neden.name);
    neden = neden.cause;
  }
  if (zincir.length > 0) alan.nedenZinciri = zincir;

  if (yigitIzi && typeof hata.stack === 'string') {
    alan.yigitIzi = hata.stack.split('\n')[0];
  }
  return alan;
}
```

Hata Yönetimi Stratejisi dersinde kurulan neden zinciri burada karşılığını buluyor:
sarmalanmış hatanın altındaki kodlar tek bir dizi alanında toplanıyor.

```js
// kutuk-hata-dene.mjs
import { readFile } from 'node:fs/promises';
import { kutukOlustur } from './kutuk.mjs';
import { hataAlanlari } from './kutuk-hata.mjs';
import { IsletimselHata } from './hata-sinifi.mjs';

const kutuk = kutukOlustur({
  hizmet: 'olcum-toplayici',
  saat: () => '2024-02-07T09:12:44.000Z',
});

try {
  await readFile('yok.ndjson', 'utf8');
} catch (dusuk) {
  const hata = new IsletimselHata('olcum dosyasi acilamadi', {
    kod: 'OLCUM_DOSYASI_YOK', durum: 503, cause: dusuk,
  });
  kutuk.hata('istek karsilanamadi', { ...hataAlanlari(hata), yol: '/ozet' });
}

kutuk.hata('beklenmeyen tip', { ...hataAlanlari(new TypeError('deger sonlu olmali'), { yigitIzi: true }) });
```

```sh
node kutuk-hata-dene.mjs
```

```
{"zaman":"2024-02-07T09:12:44.000Z","duzey":"hata","hizmet":"olcum-toplayici","ileti":"istek karsilanamadi","hataAdi":"IsletimselHata","hataIletisi":"olcum dosyasi acilamadi","hataKodu":"OLCUM_DOSYASI_YOK","nedenZinciri":["ENOENT"],"yol":"/ozet"}
{"zaman":"2024-02-07T09:12:44.000Z","duzey":"hata","hizmet":"olcum-toplayici","ileti":"beklenmeyen tip","hataAdi":"TypeError","hataIletisi":"deger sonlu olmali","yigitIzi":"TypeError: deger sonlu olmali"}
```

Birinci kayıt zincirin en altındaki `ENOENT` kodunu taşıyor; arızayı arayan kişi,
dosya sistemine kadar inen nedeni tek satırdan okuyor.

Yığıt izi varsayılan olarak yazılmıyor. Gerekçesi iki yönlüdür: iz çok satırlıdır ve
satır başına bir kayıt sözleşmesini bozar; ayrıca dosya yollarını ve dizin yapısını
kütüğe taşır. Örnekte iznin yalnızca ilk satırı alınıyor. Tamamının gerektiği
durumlarda satır sonları kaçırılarak tek bir JSON dizgi değeri olarak yazılır — ham
metin olarak değil.

İşletimsel hata ile programcı hatası kütükte de ayrılır. İşletimsel hata beklenen bir
koşuldur ve `uyari` düzeyi çoğu zaman yeter; programcı hatası her zaman `hata`
düzeyine ve tam iziyle yazılır, çünkü birinin bakması gerekir.

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

Kütük **standart çıktıya** yazılır. Süreç kendi dosyasını açmaz, kendi dosya
döndürmesini yapmaz, kendi arşivini tutmaz.

Gerekçe, Kabuk Programlama kursunda kurulan ilkeyle aynıdır: çıktının nereye
gideceğine komut değil, komutu çağıran karar verir. Süreci başlatan denetçi çıktıyı
bir dosyaya, bir toplayıcıya veya bir boru hattına yönlendirir; süreç bunu bilmez.
Böylece aynı yapı, hiçbir değişiklik olmadan farklı işletim düzenlerinde çalışır.

Tanı iletilerinin standart hataya yazılması kuralı burada bir istisnaya uğrar:
yapılandırılmış kütük veriyle karışmaz, çünkü hizmetin "verisi" HTTP yanıtlarıdır,
standart çıktı değildir. Komut satırı araçlarında ise durum tersine döner —
Komut Satırı Uygulamaları dersindeki araç, kütüğünü standart hataya yazar, çünkü
standart çıktı raporun kendisidir.

Yazma davranışının bir ayrıntısı vardır: standart çıktı bir dosyaya bağlıyken yazma
eşzamanlı, bir boruya bağlıyken eşzamansız yürütülebilir. İkinci durumda `process.exit`
ile sert çıkış, yazılmamış satırları düşürebilir — Süreç Nesnesi dersindeki uyarının
somut karşılığı budur.

## Kayıtları İncelemek

Yapılandırılmış biçimin kazancı, özel bir araç gerektirmemesidir:

```sh
node kutuk-dene.mjs > kutuk.ndjson
grep -o '"duzey":"[a-z]*"' kutuk.ndjson | sort | uniq -c | sort -rn
```

```
   2 "duzey":"bilgi"
   1 "duzey":"uyari"
   1 "duzey":"hata"
```

Alan çıkarmak gerektiğinde kısa bir süzgeç yeter:

```sh
grep '"duzey":"hata"' kutuk.ndjson | node -e "
const satirlar = require('node:fs').readFileSync(0, 'utf8').trim().split('\n');
for (const s of satirlar) { const k = JSON.parse(s); console.log(k.kod, '|', k.ileti); }
"
```

```
ENOENT | olcum dosyasi acilamadi
```

İkinci örnekte `readFileSync(0, 'utf8')` çağrısı, 0 numaralı dosya tanıtıcısını —
yani standart girdiyi — okur. Kabuk Programlama kursunda tanıtılan tanıtıcı
numaraları burada doğrudan kullanılabilir.

Son bir kural: kütüğe kişisel veri ve gizli değer yazılmaz. Yapılandırma Yönetimi
dersinde maskelenen yönetim anahtarı, bir hata nesnesinin alanı olarak kütüğe
sızabilir. Kayda yazılacak alanların açıkça seçilmesi — hata nesnesinin bütününü
serpiştirmek yerine — bu sızıntıyı önler.

## Özet

- Kütük satırı hem insana hem programa hitap eder; satır başına bir JSON nesnesi
  biçimi iki okuyucuyu birden karşılar.
- İleti metni sabit tutulur, değişken bilgi ayrı alanlara konur; aramayı olanaklı
  kılan budur.
- Düzeyler sayısal ağırlıkla sıralanır ve eşik yapılandırmadan gelir; her şeyi hata
  düzeyine yazmak ayrımı anlamsızlaştırır.
- Alt kütük, verilen alanları kendisinden üretilen bütün satırlara ekler ve bir
  isteğin kayıtlarının toplanmasını sağlar.
- Hata nesnesi doğrudan yazılmaz: alanları açıkça seçilir, neden zinciri kodlara
  indirgenir ve yığıt izi ancak istendiğinde eklenir.
- Kütük standart çıktıya yazılır; dosya, döndürme ve arşiv süreci başlatan denetçinin
  işidir.

## Sonraki Adım

Ölçüm toplayıcının parçaları çoğaldı: özetleyici, yapılandırma, hata sınıfları,
kütük ve sunucu. Bunların doğru çalıştığı şimdiye kadar elle, çıktıya bakarak
denetlendi. Sonraki ders bu denetimi otomatikleştirir: çalışma zamanının yerleşik
test koşucusu ile birim ve tümleşim testlerini yazar, başarısız bir testin nasıl
göründüğünü ve kapsam raporunun ne söylediğini gösterir.
