---
title: 'Hata Yönetimi Stratejisi'
source: 'https://academia.sh/tr/kurslar/nodejs/hata-yonetimi-stratejisi'
course: 'Node.js Çalışma Zamanı'
language: tr
updated: '2026-08-17T18:09:51+00:00'
license: 'CC BY-SA 4.0'
---

# Hata Yönetimi Stratejisi

İşletimsel hata ile programcı hatasının ayrımı, hata nedenlerinin zincirlenmesi, eşzamansız hataların yakalanamayan biçimleri ve son çare kancalarının doğru kullanımı.

Önceki ders, yapılandırma hatasının sürecin açılmasını engellemesi gerektiğini
söyledi. Çalışma sırasında ortaya çıkan her hata aynı davranışı hak etmez: bir
isteğin bozuk gövdesi hizmeti kapatmamalı, ama bellekte bozulmuş bir durum onu
kapatmalıdır.

Bu dersin sorusu şudur: hangi hata yakalanır, hangisi sürecin sonlanmasına yol açar?
Ayrım tek bir ölçüte dayanır ve stratejinin tamamı ondan çıkar.

## İki Hata Sınıfı

**İşletimsel hata**, doğru yazılmış bir programın çalışma koşullarında karşılaşmayı
beklediği durumdur: dosya yok, ağ ulaşılamıyor, istemci geçersiz veri gönderdi, disk
doldu. Bunlar programın kusuru değildir; öngörülmüş ve ele alınmıştır.

**Programcı hatası**, kodun kendi sözleşmesini ihlal etmesidir: tanımsız bir değerin
alanına erişmek, zorunlu bir argümanı geçirmeyi unutmak, bir sayı beklenen yere nesne
vermek. Bunlar öngörülmemiştir ve programın o andaki durumu hakkındaki varsayımların
yanlış olduğunu gösterir.

Ayrımın kuralı şudur: **işletimsel hata ele alınır, programcı hatası ele alınmaz.**
İkincisini yakalayıp devam etmek, bozulmuş bir durumla çalışmaya devam etmek
demektir; ne kadar sürdüğü belli olmayan ve tanısı çok daha zor bir arıza üretir.

Ayrımın kodda görünür olması için işletimsel hatalar kendi tipiyle işaretlenir:

```js
// hata-sinifi.mjs
export class IsletimselHata extends Error {
  constructor(ileti, { kod, durum = 500, cause } = {}) {
    super(ileti, { cause });
    this.name = 'IsletimselHata';
    this.kod = kod;
    this.durum = durum;
    this.isletimsel = true;
  }
}

export function isletimselMi(hata) {
  return hata instanceof IsletimselHata && hata.isletimsel === true;
}
```

## Nedeni Zincirlemek

Alt katmandaki bir hata üst katmana taşınırken çevrilir: üst katman kendi diliyle
konuşan bir hata fırlatır, ama asıl nedeni kaybetmez. `cause` seçeneği bu bağı kurar.

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

async function olcumleriOku(yol) {
  try {
    return await readFile(yol, 'utf8');
  } catch (hata) {
    if (hata.code === 'ENOENT') {
      throw new IsletimselHata(`olcum dosyasi yok: ${yol}`, {
        kod: 'OLCUM_DOSYASI_YOK', durum: 503, cause: hata,
      });
    }
    throw hata;
  }
}

for (const yol of ['olcumler.ndjson', 'yok.ndjson']) {
  try {
    const metin = await olcumleriOku(yol);
    console.log(`${yol}: ${metin.trim().split('\n').length} satir`);
  } catch (hata) {
    console.log(`${yol}: isletimsel mi = ${isletimselMi(hata)}, kod = ${hata.kod}, durum = ${hata.durum}`);
    console.log(`  asil neden: ${hata.cause.code}`);
  }
}

// Programci hatasi: sozlesme ihlali, yakalanip yutulmaz
try {
  null.deger;
} catch (hata) {
  console.log(`programci hatasi: isletimsel mi = ${isletimselMi(hata)}, tur = ${hata.constructor.name}`);
}
```

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

```
olcumler.ndjson: 12 satir
yok.ndjson: isletimsel mi = true, kod = OLCUM_DOSYASI_YOK, durum = 503
  asil neden: ENOENT
programci hatasi: isletimsel mi = false, tur = TypeError
```

Üç ayrıntı önemlidir.

Yalnızca tanınan hata kodu çevriliyor; geri kalanı olduğu gibi yukarı fırlatılıyor.
Bütün hataları tek bir tipe çevirmek, bilinmeyen arızaları bilinen bir kategoriye
sokarak gizler.

Çevrilen hata kendi kodunu taşıyor. Çağıran, ileti metnini ayrıştırmadan karar
verebilir; ileti metni değişse de kod sabit kalır.

Asıl neden erişilebilir durumda. Tanı sırasında zincirin en altındaki sistem çağrısı
hatasına ulaşmak, arızayı bulmanın en kısa yoludur.

## Hizmette Karşılığı

HTTP Sunucusu dersindeki hata yakalayıcı her hatayı 400 olarak bildiriyordu. Ayrım
kurulduktan sonra doğru eşleme yapılabilir:

```js
// hata-sunucu.mjs
import { createServer } from 'node:http';
import { IsletimselHata, isletimselMi } from './hata-sinifi.mjs';
import { satirdanKayit } from './ozetleyici.mjs';

function hataYaniti(hata, yanit) {
  if (isletimselMi(hata)) {
    yanit.writeHead(hata.durum, { 'content-type': 'application/json' });
    yanit.end(JSON.stringify({ hata: hata.message, kod: hata.kod }) + '\n');
    return;
  }
  // Programci hatasi: ayrinti disariya verilmez, iceride tam kaydedilir
  process.stderr.write(`programci hatasi: ${hata.stack.split('\n')[0]}\n`);
  yanit.writeHead(500, { 'content-type': 'application/json' });
  yanit.end(JSON.stringify({ hata: 'ic hata', kod: 'IC_HATA' }) + '\n');
}

const sunucu = createServer(async (istek, yanit) => {
  try {
    if (istek.url === '/olcum') {
      const parcalar = [];
      for await (const p of istek) parcalar.push(p);
      const govde = Buffer.concat(parcalar).toString('utf8');
      try {
        satirdanKayit(govde.trim());
      } catch (hata) {
        throw new IsletimselHata('gecersiz olcum kaydi', {
          kod: 'GECERSIZ_KAYIT', durum: 400, cause: hata,
        });
      }
      yanit.writeHead(201, { 'content-type': 'application/json' });
      yanit.end('{"alinan":1}\n');
      return;
    }
    if (istek.url === '/coken') {
      null.deger;                       // bilerek programci hatasi
    }
    throw new IsletimselHata('bulunamadi', { kod: 'YOL_YOK', durum: 404 });
  } catch (hata) {
    hataYaniti(hata, yanit);
  }
});

sunucu.listen(8791, '127.0.0.1', () => console.log('dinleniyor'));
```

Sunucu çalışırken dört istek gönderelim:

```sh
curl -s -X POST --data-binary 'bozuk' -w 'durum: %{http_code}\n' http://127.0.0.1:8791/olcum
```

```
{"hata":"gecersiz olcum kaydi","kod":"GECERSIZ_KAYIT"}
durum: 400
```

```sh
curl -s -w 'durum: %{http_code}\n' http://127.0.0.1:8791/yok
```

```
{"hata":"bulunamadi","kod":"YOL_YOK"}
durum: 404
```

```sh
curl -s -w 'durum: %{http_code}\n' http://127.0.0.1:8791/coken
```

```
{"hata":"ic hata","kod":"IC_HATA"}
durum: 500
```

Son çağrıda sunucunun uçbiriminde şu satır yazıldı:

```
programci hatasi: TypeError: Cannot read properties of null (reading 'deger')
```

İşletimsel hatanın iletisi istemciye veriliyor, programcı hatasınınki verilmiyor. Bu
bir gizleme alışkanlığı değil, iki gerekçeye dayanan bir karardır: yığıt izi ve iç
ileti, dosya yollarını ve kod yapısını dışarıya sızdırır; ayrıca istemcinin
yapabileceği bir şey yoktur — 500 yanıtının tek anlamlı yanıtı yeniden denemek veya
bildirmektir.

## Eşzamansız Hatanın Yakalanamadığı Yerler

`try`/`catch` yalnızca kendi eşzamanlı gövdesini ve beklenen sözleri kapsar. Bir geri
çağrının içinde fırlatılan hata bu kapsamın dışındadır.

```js
// yakalanmayan.mjs
import { readFile } from 'node:fs/promises';

// try/catch, geri cagriya ertelenen hatayi yakalamaz
try {
  setTimeout(() => { throw new Error('geri cagri icinde firlatildi'); }, 0);
} catch (hata) {
  console.log('bu satir calismaz');
}

// Beklenmeyen reddetme
readFile('yok.ndjson', 'utf8');

console.log('senkron akis bitti');
```

```sh
node yakalanmayan.mjs
```

Çıktının ilk satırı `senkron akis bitti` olur; ardından yakalanmayan hata yazılır ve
süreç sıfırdan farklı bir kodla sonlanır. `bu satir calismaz` satırı hiç yazılmaz:
`setTimeout` çağrısı hemen döndü, hata ise çok sonra, döngünün zamanlayıcı
aşamasında fırlatıldı — o anda `try` bloğu çoktan bitmişti.

Aynı dosyadaki ikinci sorun daha sinsidir. `readFile` çağrısı beklenmediği için
döndürdüğü sözün reddi hiçbir yerde ele alınmaz. Bu, **işlenmemiş reddetme**dir ve
varsayılan davranışı süreci sonlandırmaktır:

```js
// reddetme.mjs
import { readFile } from 'node:fs/promises';

readFile('yok.ndjson', 'utf8');     // donen soz beklenmiyor, hatasi da islenmiyor
console.log('senkron akis bitti');
```

```sh
node reddetme.mjs > /dev/null; echo "cikis kodu: $?"
```

```
cikis kodu: 1
```

Sürecin sonlanması sert görünür ama doğrudur: sessizce yutulan bir reddetme, verinin
yazıldığı sanılırken yazılmamış olmasına yol açar. Kural şudur: **söz döndüren her
çağrı ya beklenir ya da reddi açıkça ele alınır.**

## Son Çare Kancaları

İki kanca, yakalanmamış hataları programın eline verir:

```js
// son-care.mjs
process.on('uncaughtException', (hata, koken) => {
  process.stderr.write(`olumcul: ${hata.message} (koken: ${koken})\n`);
  process.exit(1);            // durum bozulmus sayilir: yeniden baslat
});

process.on('unhandledRejection', (neden) => {
  process.stderr.write(`islenmemis reddetme: ${neden.message ?? neden}\n`);
  process.exit(1);
});

setTimeout(() => { throw new Error('gec gelen hata'); }, 0);
console.log('kanca kuruldu');
```

```sh
node son-care.mjs; echo "cikis kodu: $?"
```

```
kanca kuruldu
olumcul: gec gelen hata (koken: uncaughtException)
cikis kodu: 1
```

Bu kancaların tek meşru kullanımı, sürecin ölmeden önce arızayı kaydetmesidir. Kancayı
kurup çalışmaya devam etmek, çalışma zamanının varsayılan davranışını bozar ve
bilinmeyen bir durumda ilerlemeye izin verir.

Kancanın içinde uzun iş yapılmaz. Süreç zaten şüpheli bir durumdadır; kancada
eşzamansız bir kütük yazımı başlatmak, yazının tamamlanacağının güvencesi yoktur.
Kısa ve eşzamanlı bir satır yazıp çıkmak en güvenlisidir.

Süreç öldükten sonra hizmetin ayakta kalması, sürecin kendi sorumluluğu değildir. Bu
işi, süreci yeniden başlatan dış bir denetçi üstlenir; Süreç Yönetimi ve Sağlamlık
dersinde ele alınacak.

## Özet

- İşletimsel hata öngörülmüş bir çalışma koşuludur ve ele alınır; programcı hatası
  sözleşme ihlalidir ve ele alınmaz.
- İşletimsel hatalar kendi tipleriyle işaretlenir ve kod alanı taşır; çağıran karar
  verirken ileti metnini ayrıştırmaz.
- Hata çevrilirken `cause` ile asıl neden korunur; yalnızca tanınan durumlar çevrilir,
  geri kalanı olduğu gibi yukarı fırlatılır.
- Geri çağrı içinde fırlatılan hata ve beklenmeyen sözün reddi `try`/`catch` kapsamına
  girmez; ikincisinin varsayılan sonucu sürecin sonlanmasıdır.
- Son çare kancaları yalnızca kaydetmek ve çıkmak için kullanılır; kancayı kurup
  devam etmek bilinmeyen bir durumda ilerlemektir.

## Sonraki Adım

Hataların kaydedilmesinden birkaç kez söz edildi ama nereye ve hangi biçimde
yazılacağı belirlenmedi. Serbest metin bir ileti, arızayı arayan kişinin işine
yaramaz: arama, süzme ve sayma gerektiren bir kayıt biçimi gerekir. Sonraki ders
yapılandırılmış günlüklemeyi kurar ve düzey yönetimini ekler.
