İçeriğe geç
academia.sh

Ders 27 / 27

Hata İzleme

İstemci hatalarının yakalandığı noktalar, kaynak haritasıyla yığıt izinin okunur kılınması, parmak izi üreterek gruplama, bir grubun etkisinin ölçülmesi ve gürültü ile gizlilik kararları.

İçindekiler

Ölçütler uygulamanın ne kadar hızlı olduğunu söyler; çalışıp çalışmadığını söylemez. Ölçüm tablosu çizilirken bir istisna fırlarsa, o oturumun süre ölçütleri iyi görünmeyi sürdürür. Kullanıcı boş bir ekrana bakar ve çoğu zaman kimseye haber vermez.

Bu ders, o sessiz başarısızlıkları görünür kılan düzeneği kurar: hatanın yakalanması, gönderilmesi ve binlerce olayın tek tek değil, aynı kökene inen gruplar hâlinde okunması.

Yakalama Noktaları

İstemci hataları tek bir yerden gelmez. Dört ayrı kaynak vardır ve her birinin kendi sınırı bulunur.

Yakalanmayan istisnalar belge düzeyindeki error olayıyla bildirilir; olay nesnesi bir ErrorEvent örneğidir ve iletiyi, kaynağı, konumu ve hata nesnesini taşır. Reddedilmiş sözlerin işlenmemiş olanları unhandledrejection olayıyla gelir — Eşzamansız JavaScript kursunda tanımlanan işlenmeyen reddetme kavramının tarayıcıdaki karşılığı budur. Kaynak yükleme hataları kabarmaz; yalnız yakalama evresinde dinlenerek görülebilir. Dördüncüsü uygulamanın kendi bildirdiği hatalardır: yakalanmış ama kurtarılamamış durumlar.

// hata-toplayici.js — istemci hatalarinin toplanmasi
const uc = '/hata';
const ortak = { surum: '2.4.1', yol: location.pathname };

function bildir(tur, ileti, yigitIzi) {
  navigator.sendBeacon(uc, JSON.stringify({ ...ortak, tur, ileti, yigitIzi }));
}

addEventListener('error', (olay) => {
  if (olay.target !== window) {           // kaynak yukleme hatasi
    bildir('kaynak', `${olay.target.tagName} yuklenemedi`, olay.target.src ?? '');
    return;
  }
  bildir('istisna', olay.message, olay.error?.stack ?? '');
}, true);

addEventListener('unhandledrejection', (olay) => {
  const neden = olay.reason;
  bildir('reddetme', String(neden?.message ?? neden), neden?.stack ?? '');
});

Bir sınır baştan bilinmelidir. Farklı bir kaynaktan yüklenen bir betikten fırlayan hata, tarayıcı tarafından ayrıntısız verilir: ileti yalnızca betik hatası olduğunu söyler, yığıt izi boştur. Bunun nedeni kaynak yalıtımıdır — Kaynaklar Arası Paylaşım dersindeki kurallar burada da işler. Ayrıntının açılması için betiğin kaynaklar arası olarak istenmesi ve sunucunun buna izin veren başlığı göndermesi gerekir.

İkinci sınır, bileşen düzeyidir. Bileşen Tabanlı Arayüz Geliştirme kursundaki hata sınırı, çizim sırasında fırlayan hatayı yakalayıp yedek bir arayüz gösterir; bu hatalar belge düzeyindeki dinleyiciye ulaşmaz ve ayrıca bildirilmeleri gerekir.

Yığıt İzini Okunur Kılmak

Üretim yapısındaki kod küçültülmüştür: fonksiyon adları tek harfe inmiş, satırlar birleşmiştir. Böyle bir yığıt izi okunamaz. Çözüm Modüller, Araçlar ve Ekosistem kursunda tanımlanan kaynak haritasıdır: küçültülmüş konumu özgün dosya, satır ve ada geri eşleyen tablo.

Eşleme istemcide değil, hataları toplayan tarafta yapılır. Kaynak haritası yayımlanan varlıklarla birlikte dağıtılmaz — özgün kaynağı okunur kılar. Yapı sırasında ayrıca üretilip yalnız toplama tarafına yüklenir.

Gruplama

Tek bir kusur binlerce olay üretir. Aynı satırdaki bir hata, listeyi açan her kullanıcıda bir kez fırlar; okunabilir olması için olayların aynı kökene inenlerinin tek bir grupta toplanması gerekir. Bunun anahtarına parmak izi denir.

Parmak izi, olayın kararlı parçalarından üretilir ve kararsız parçalar dışarıda bırakılır. Yapı özeti taşıyan dosya adı, satır ve sütun numaraları, sorgu dizgileri, iletideki kimlik ve sayı değerleri her yayımda ya da her olayda değişir; bunlar anahtara girerse aynı kusur onlarca gruba dağılır.

// parmak-izi.mjs — yigit izinden gruplama anahtari uretir
const CERCEVE = /^\s*at\s+(?:(.+?)\s+\()?([^()]+?)\)?$/;

export function cerceveler(yigitIzi) {
  return yigitIzi
    .split('\n')
    .slice(1)
    .map((satir) => satir.match(CERCEVE))
    .filter(Boolean)
    .map((e) => ({ islev: e[1] ?? '<anonim>', kaynak: e[2] }));
}

export function kaynagiNormallestir(kaynak) {
  if (kaynak === '<anonymous>' || kaynak.startsWith('node:')) return kaynak;
  return kaynak
    .replace(/^[a-z]+:\/\/[^/]+/, '')           // koken
    .replace(/[?#][^:]*$/, '')                  // sorgu ve parca
    .replace(/:\d+:\d+$/, '')                   // satir ve sutun
    .replace(/\.[0-9a-f]{6,}\.(m?js)$/, '.$1'); // yapi ozeti
}

export function iletiyiNormallestir(ileti) {
  return ileti
    .replace(/\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/gi, '<kimlik>')
    .replace(/\b\d+\b/g, '<sayi>');
}

function ozet(metin) {
  let h = 0x811c9dc5;
  for (const kod of metin) {
    h ^= kod.codePointAt(0);
    h = Math.imul(h, 0x01000193) >>> 0;
  }
  return h.toString(16).padStart(8, '0');
}

export function parmakIzi(olay, { derinlik = 3 } = {}) {
  const [ilkSatir] = olay.yigitIzi.split('\n');
  const [tur, ...kalan] = ilkSatir.split(': ');
  const anahtar = [
    tur,
    iletiyiNormallestir(kalan.join(': ')),
    ...cerceveler(olay.yigitIzi)
      .slice(0, derinlik)
      .map((c) => `${c.islev}@${kaynagiNormallestir(c.kaynak)}`),
  ].join('|');
  return { anahtar, ozet: ozet(anahtar) };
}

export function grupla(olaylar) {
  const gruplar = new Map();
  for (const olay of olaylar) {
    const { anahtar, ozet: kimlik } = parmakIzi(olay);
    const grup = gruplar.get(kimlik) ?? { kimlik, anahtar, sayi: 0, oturumlar: new Set(), surumler: new Set() };
    grup.sayi += 1;
    grup.oturumlar.add(olay.oturum);
    grup.surumler.add(olay.surum);
    gruplar.set(kimlik, grup);
  }
  return [...gruplar.values()].sort((a, b) => b.sayi - a.sayi);
}

Aşağıdaki altı olay, aynı kusurun farklı yayımlardan ve farklı oturumlardan gelen örneklerini içeriyor.

// rapor.mjs — alti hata olayinin gruplanmasi
import { grupla } from './parmak-izi.mjs';

const OLAYLAR = [
  { oturum: 's-1', surum: '2.4.0', yigitIzi:
`TypeError: Cannot read properties of undefined (reading 'deger')
    at satirCiz (https://kuzey-yamac.ornek/varlik/tablo.4f2a9c.js:12:31)
    at Array.map (<anonymous>)
    at OlcumTablosu (https://kuzey-yamac.ornek/varlik/tablo.4f2a9c.js:40:22)` },
  { oturum: 's-2', surum: '2.4.1', yigitIzi:
`TypeError: Cannot read properties of undefined (reading 'deger')
    at satirCiz (https://kuzey-yamac.ornek/varlik/tablo.9b1d70.js:12:47)
    at Array.map (<anonymous>)
    at OlcumTablosu (https://kuzey-yamac.ornek/varlik/tablo.9b1d70.js:41:22)` },
  { oturum: 's-3', surum: '2.4.1', yigitIzi:
`TypeError: Cannot read properties of undefined (reading 'deger')
    at satirCiz (https://kuzey-yamac.ornek/varlik/tablo.9b1d70.js:12:47?v=3)
    at Array.map (<anonymous>)
    at OlcumTablosu (https://kuzey-yamac.ornek/varlik/tablo.9b1d70.js:41:22)` },
  { oturum: 's-2', surum: '2.4.1', yigitIzi:
`Error: olcum 84213 kaydedilemedi (400)
    at gonder (https://kuzey-yamac.ornek/varlik/form.1c8e55.js:88:11)` },
  { oturum: 's-4', surum: '2.4.1', yigitIzi:
`Error: olcum 91007 kaydedilemedi (400)
    at gonder (https://kuzey-yamac.ornek/varlik/form.1c8e55.js:88:11)` },
  { oturum: 's-5', surum: '2.4.1', yigitIzi:
`TypeError: sozlesme.gonder is not a function
    at kaydet (https://kuzey-yamac.ornek/varlik/form.1c8e55.js:120:8)` },
];

for (const g of grupla(OLAYLAR)) {
  console.log(`${g.kimlik}  olay=${g.sayi}  oturum=${g.oturumlar.size}  surum=${[...g.surumler].join(',')}`);
  console.log(`          ${g.anahtar}`);
}
node rapor.mjs
1c081b4b  olay=3  oturum=3  surum=2.4.0,2.4.1
          TypeError|Cannot read properties of undefined (reading 'deger')|satirCiz@/varlik/tablo.js|Array.map@<anonymous>|OlcumTablosu@/varlik/tablo.js
bb0e50c8  olay=2  oturum=2  surum=2.4.1
          Error|olcum <sayi> kaydedilemedi (<sayi>)|gonder@/varlik/form.js
8a4701e6  olay=1  oturum=1  surum=2.4.1
          TypeError|sozlesme.gonder is not a function|kaydet@/varlik/form.js

Altı olay üç gruba indi. Birinci grubun üç örneği farklı yapı özetleri, farklı satır ve sütun numaraları ve birinde bir sorgu dizgisi taşıyordu; normalleştirme bunları eledi. İkinci grubun iki örneği farklı ölçüm numaraları içeriyordu; sayılar yer tutucuya çevrildiği için tek kusur olarak göründüler.

Gruplama iki yönde yanlış ayarlanabilir. Aşırı gruplama, birbirinden bağımsız kusurları tek satırda toplar: iletiyi bütünüyle atıp yalnız hata türüne bakmak buna yol açar. Yetersiz gruplama ise tek kusuru yayım başına ayrı gruplara böler ve hiçbirinin etkisi görünmez olur. Ayarın ölçüsü kullanışlılıktır: bir grup, tek bir düzeltmeyle kapanabiliyorsa doğru çizilmiştir.

Bir Grubun Okunması

Grubun taşıdığı sayılar önceliklendirmeyi belirler. Olay sayısı kusurun ne sıklıkta fırladığını, etkilenen oturum sayısı kaç kişiye dokunduğunu söyler. İkisi ayrılmalıdır: tek bir oturumda döngü içinde binlerce kez fırlayan bir hata, olay sayısında listenin başına çıkar ama bir kullanıcıyı ilgilendirir. Sıralama oturum sayısına göre yapılır.

Sürüm dağılımı ikinci bilgidir. Bir grup yalnız son sürümde görünüyorsa, kusur o yayımla gelmiştir ve geri alma bir seçenektir. İlk görülme ve son görülme zamanları aynı soruyu zaman ekseninde yanıtlar.

Gürültü ayrı bir iştir. Tarayıcı eklentilerinden ve dış betiklerden gelen hatalar uygulamanın kodunda değildir; yığıt izinin ilk çerçevesi uygulamanın alan adına ait değilse süzülebilir. Kullanıcı sayfadan ayrılırken iptal edilen isteklerin ürettiği hatalar da gerçek kusur değildir.

Hacim denetlenmelidir. Bir döngü içinde fırlayan hata tek oturumdan binlerce olay gönderebilir; oturum başına hız sınırı ve örnekleme bunu keser. Örnekleme yapılırken gönderilmeyen olayların sayısı da iletilir, yoksa etki ölçüsü bozulur.

Gizlilik son karardır. Hata iletisi kullanıcının yazdığı metni içerebilir, adres kimlik taşıyabilir. Toplayıcı, bilinen kimlik biçimlerini ve serbest metni gönderimden önce maskeler. Kişisel veri, toplandıktan sonra silinmesi zor olan veridir.

Özet

  • İstemci hataları dört noktadan gelir: yakalanmayan istisnalar, işlenmeyen söz reddetmeleri, kaynak yükleme hataları ve uygulamanın kendi bildirdikleri.
  • Farklı kaynaktan gelen betiğin hatası ayrıntısız verilir; ayrıntı için kaynaklar arası istek ve izin başlığı gerekir. Yığıt izi kaynak haritasıyla toplama tarafında çözülür.
  • Parmak izi, hata türü ile normalleştirilmiş ileti ve ilk birkaç çerçeveden üretilir; yapı özeti, satır ve sütun numarası, sorgu dizgisi ve iletideki sayılar dışarıda bırakılır.
  • Altı olay üç gruba indi: farklı yayımlardan ve farklı ölçüm numaralarından gelen örnekler tek kusur olarak göründü.
  • Önceliklendirme olay sayısına değil etkilenen oturum sayısına göre yapılır; gürültü süzülür, hacim sınırlanır ve kişisel veri gönderimden önce maskelenir.

Kurs Kapanışı

Frontend Kalitesi kursu dört konuyu aynı ilkeye bağladı: ölçülmeyen kalite iddiası yoktur. Başarım konusu kullanıcı merkezli ölçütleri tanımladı ve bunları bir bütçeye bağladı. Erişilebilirlik konusu ölçütleri standart düzeyinde ele aldı ve otomatik denetimin nerede bitip elle doğrulamanın nerede başladığını gösterdi. Güvenlik konusu istemci tarafındaki riskleri savunma katmanlarına dönüştürdü. Bu son konu ise üçünün de ortak sorusunu yanıtladı: kurulan şey bozulduğunda bunu ne haber verir? Yanıt iki yönlüydü — değişiklikten önce çalışan testler ve yayımdan sonra çalışan izleme. İkisi arasında bir döngü vardır: alanda görülen bir kusur, onu bir daha geçirmeyecek bir teste dönüşür; testin gözden kaçırdığı bir durum, alanda bir hata grubu olarak geri gelir.

Bu kursla birlikte Frontend Geliştirme müfredatı da kapanıyor. Yol belgeden başladı — HTML ile yapının kurulması ve tarayıcının o yapıyı nasıl işlediği. Sonra biçeme geçti: basamaklama, kutu modeli ve görsel özellikler. Düzen sistemleri ve duyarlı tasarım, yapının farklı ekran ve girdi koşullarına uyarlanmasını ele aldı. Tarayıcı ve web platformu, sayfayı bir belge olmaktan çıkarıp davranışı olan bir uygulamaya çevirdi. Bileşen tabanlı geliştirme bu davranışı yeniden kullanılabilir parçalara böldü; uygulama mimarisi o parçaları yönlendirme, durum ve veri erişimiyle bir bütün hâline getirdi. Oluşturma stratejileri, aynı uygulamanın sunucuda mı istemcide mi, ne zaman ve hangi maliyetle üretileceğini sordu. Ve bu son kurs, bütün bu kararların doğru kaldığının nasıl bilineceğini gösterdi: belgeden biçeme, düzenden davranışa, bileşenden mimariye, oluşturmadan kaliteye.

Müfredat boyunca bir soru bilerek dışarıda bırakıldı. Bir düğmenin nasıl çalışacağı, hangi rolü taşıyacağı, hangi durumlarda hangi ölçüte uyacağı anlatıldı; o düğmenin neden o boyutta, o renkte ve orada durduğu anlatılmadı. Aralık ölçeğinin, tipografik ölçeğin, renk rollerinin ve durum tasarımının gerekçeleri kendi başına bir alandır. Arayüz Tasarımı ve Tasarım Sistemleri müfredatı bu soruyu ele alır: görsel ve etkileşimsel kararları gerekçelendirilebilir ilkelere bağlar ve bu kararları ölçeklenen bir sisteme dönüştürür. İlk kursu olan Arayüz Tasarımının Temelleri, bu müfredatla birlikte okunmaya uygundur — burada kurulan bileşenlerin görünüşüne dair kararlar orada gerekçesini bulur.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat