Ders 05 / 18
Günlük Ayrıştırma ve Yapılandırma
Gerçek günlük satırları ayrıştırılıp aranabilir alanlara çevrilir ve iki depo biçimi soru kümesine karşı sayılır: yapılandırılmamış metin kaç soruyu yanıtlıyor, alanlar üretilince kaç soru açılıyor. Biçim değiştiğinde ayrıştırma kuralının sessizce kaçırdığı satır sayılır ve yanlış çıkan yanıtın bedeli yazılır.
İçindekiler
Bir önceki ders satırı bir bütün olarak saydı: kaç satır üretildi, kaç satır düştü, kaç bayt saklandı. Depoya ulaşan satır ise hâlâ metindir. Metin üzerinde bir dizeyi arayabilir ve kaç satırda geçtiğini sayabilirsin; hangi bölgenin okumasının hangi ortamda doğrulamadan geçmediğini soramazsın, çünkü bunun için satırın parçalara ayrılmış olması gerekir.
Bu ders o adımı ölçer. Gerçek satırlar bir ayrıştırma kuralından geçirilir, aranabilir alanlara çevrilir ve iki depo biçimi — ham metin ile alanlar — aynı soru kümesine karşı sayılır. Üçüncü ölçü kuralın kaçırdığı satırdır: bir servis biçim değiştirdiğinde alanlar sessizce boş kalır ve sorgu hata vermeden yanlış yanıt döndürür.
Satırlar bölgesel ölçüm ağının gecelik doğrulama akışındandır ve kurgudur; her satırın doğru karşılığı ayrıca tutulur ve ölçüm ona karşı yapılır.
OG25. Doğrulama akışının 900 satırı kendi üretecimizle üretilir, tohum 20260903; her satırın
yapılandırılmış karşılığı ayrıca saklanır ve doğruluk ona göre denetlenir.
OG26. Doğrulayıcı servisi, satırların dörtte birlik noktasından sonra yeni bir sürüme geçer ve
alan adlarını değiştirir: bolge yerine alan, sonuc yerine durum, sure=…ms yerine
sure_ms=….
OG27. Ayrıştırma kuralı eski biçime göre yazılmıştır ve sürüm geçişinden sonra değiştirilmez.
OG28. 07 bölgesinin düşen okumalarının çoğunu doğrulayıcı yazar; 13 bölgesinin düşenlerini
öteki servisler yazar.
OG29. Yapılandırılmamış metin yalnız önceden bilinen tek bir dizeyi sayabilir; gruplama ve
sayısal işlem alan gerektirir.
OG30. Bir alan, gruplama ya da sayısal soruda kullanılabilmesi için satırların en az yüzde
80’inde dolu olmalıdır.
Gerçek Günlük Satırları
// satir.mjs — gercek gunluk satirlari uretilir; her satirin dogru karsiligi ayrica tutulur. export const uretec = (t) => () => (t = (t * 1103515245 + 12345) % 2147483648) / 2147483648; export const BOLGE = ["01", "03", "05", "07", "09", "11", "13", "15", "17", "19", "21"]; export const DUSME = { "07": [0.80, 0.10], "13": [0.12, 0.26] }; export const SERVIS = ["okuma-toplayici", "dogrulayici", "faturalama", "is-emri"]; const iki = (n) => String(n).padStart(2, "0"); // dogrulayici yeni surume gectikten sonra alan adlarini degistirir: bolge->alan, sonuc->durum export const uret = (adet = 900, tohum = 20260903) => { const rast = uretec(tohum), satir = [], kayit = []; for (let i = 0; i < adet; i++) { const servis = SERVIS[Math.floor(rast() * SERVIS.length)]; const yeniBicim = servis === "dogrulayici" && i > adet * 0.25; // surum gecisi const b = BOLGE[Math.floor(rast() * BOLGE.length)]; // dusme orani bolgeye gore: [dogrulayicida, oteki servislerde] const [dd, od] = DUSME[b] ?? [0.10, 0.07]; const dustu = rast() < (servis === "dogrulayici" ? dd : od); const sure = 60 + Math.floor(rast() * 320), ornek = `u${iki(Math.floor(rast() * 29))}`; const zaman = `2026-08-02T02:${iki(Math.floor(i / 15))}:${iki(i % 60)}Z`; kayit.push({ servis, bolge: b, sonuc: dustu ? "dustu" : "gecti", sure, ornek, yeniBicim }); satir.push(yeniBicim ? `${zaman} ortam=uretim servis=${servis} ornek=${ornek} duzey=info olay=okuma` + ` alan=${b} durum=${dustu ? "dustu" : "gecti"} sure_ms=${sure}` : `${zaman} uretim ${servis}[${ornek}] INFO okuma islendi bolge=${b}` + ` sonuc=${dustu ? "dustu" : "gecti"} sure=${sure}ms`); } return { satir, kayit }; };
İki biçim de aynı bilgiyi taşır; fark yalnız adlarındadır. Bir günlükleme sözleşmesi değişikliğinin en sık görülen sonucu budur ve hiçbir servis için hata değildir — her iki satır da geçerlidir.
Ayrıştırma ve Kaçırılan Satır
// ayristir.mjs — yapilandirilmamis metin ile aranabilir alanlarin soru kumesine karsi karsilastirmasi. import { uret, SERVIS } from "./satir.mjs"; const { satir, kayit } = uret(); console.log(`uretilen gunluk satiri: ${satir.length}`); console.log("ornek satirlar"); for (const i of [0, 1, 227, 232]) console.log(` ${satir[i]}`); // eski bicime gore yazilmis ayristirma kurali const KURAL = /^(\S+) (\w+) ([\w-]+)\[(\w+)\] (\w+) (.*?) bolge=(\d+) sonuc=(\w+) sure=(\d+)ms$/; const alan = satir.map((s) => { const m = KURAL.exec(s); return m ? { zaman: m[1], ortam: m[2], servis: m[3], ornek: m[4], bolge: m[7], sonuc: m[8], sure: +m[9] } : { zaman: s.slice(0, 20).trim(), ortam: "", servis: "", ornek: "", bolge: "", sonuc: "", sure: null }; }); const eslesen = alan.filter((a) => a.bolge).length; console.log(`\nayristirma kurali ${eslesen}/${satir.length} satiri esledi;` + ` ${satir.length - eslesen} satirda alanlar sessizce bos (%${(100 * (satir.length - eslesen) / satir.length).toFixed(1)})`); // S7: hangi bolgenin okumasi dogrulanmadi const say = (kaynak, bAl, sAl) => { const t = {}; for (const k of kaynak) { const b = bAl(k); if (!b) continue; t[b] ??= [0, 0]; t[b][0]++; if (sAl(k) === "dustu") t[b][1]++; } return t; }; const enKotu = (t) => Object.entries(t).sort((a, b) => b[1][1] - a[1][1])[0]; const gercek = say(kayit, (k) => k.bolge, (k) => k.sonuc), cikan = say(alan, (a) => a.bolge, (a) => a.sonuc); const [gb, gv] = enKotu(gercek), [cb, cv] = enKotu(cikan); console.log("\nS7 'hangi bolgenin okumasi dogrulanmadi'"); console.log(` gercek bolge ${gb} ${gv[1]} dusen / ${gv[0]} okuma`); console.log(` ayristirmadan bolge ${cb} ${cv[1]} dusen / ${cv[0]} okuma` + ` sapma %${(100 * Math.abs(cv[1] - gv[1]) / gv[1]).toFixed(1)} yanit ayni mi: ${gb === cb ? "evet" : "hayir"}`); console.log(` kural bolge ${gb} icin ${gercek[gb][1] - (cikan[gb]?.[1] ?? 0)} dusen okumayi hic gormedi`); // yapilandirilmamis metin yalniz bilinen tek bir dizeyi sayabilir const DIZE = "okuma islendi", hamSay = satir.filter((x) => x.includes(DIZE)).length; console.log(`\nS5 'gecelik is kac okuma isledi'`); console.log(` gercek ${kayit.length} ham metinde "${DIZE}" gecen satir ${hamSay}` + ` sapma %${(100 * (kayit.length - hamSay) / kayit.length).toFixed(1)}`); // S1: servis ortalamasi alanlardan dogru cikiyor mu const ort = (l, s, f) => { const g = l.filter((x) => x.servis === s).map(f); return g.reduce((a, b) => a + b, 0) / g.length; }; const enYavas = (l, f) => SERVIS.map((s) => [s, ort(l, s, f)]).sort((a, b) => b[1] - a[1])[0]; const [gs, gd] = enYavas(kayit, (k) => k.sure), [as_, ad] = enYavas(alan.filter((a) => a.servis), (a) => a.sure); console.log(`S1 'hangi servis yavasladi' gercek ${gs} ${gd.toFixed(0)} ms` + ` ayristirmadan ${as_} ${ad.toFixed(0)} ms yanit ayni mi: ${gs === as_ ? "evet" : "hayir"}`); // bu akisin kapsamindaki alti soru; otekiler baska satir turlerinde durur const SORU = [["S1", ["servis", "sure"], "sayisal", gs === as_], ["S2", ["sure"], "sayisal", true], ["S3", ["ortam", "sonuc"], "grupla", true], ["S5", ["dize"], "say", hamSay === kayit.length], ["S7", ["bolge", "sonuc"], "grupla", gb === cb], ["S8", ["ornek"], "grupla", true]]; const kapsama = (a) => alan.filter((x) => x[a] !== "" && x[a] !== null).length / alan.length; const bayt = satir.reduce((t, x) => t + x.length, 0); const alanBayt = alan.reduce((t, a) => t + Object.values(a).join("").length + Object.keys(a).length * 2, 0); console.log("\ndepo bicimi bayt yanitlanabilen dogru yanit bayt/dogru soru yanlis yanitlanan"); const yaz = (ad2, b, y) => { const d = y.filter(([, , , dogru]) => dogru); console.log(`${ad2.padEnd(18)}${b.toLocaleString("tr-TR").padStart(6)}${`${y.length}/6`.padStart(15)}` + `${`${d.length}/6`.padStart(14)}${(d.length ? Math.round(b / d.length).toLocaleString("tr-TR") : "-").padStart(18)} ` + (y.filter((q) => !d.includes(q)).map(([k]) => k).join(" ") || "-")); }; yaz("ham metin", bayt, SORU.filter(([, , i]) => i === "say")); yaz("alanlar + dizin", bayt + alanBayt, SORU.filter(([, ger, , ]) => ger.every((g) => g === "dize" || kapsama(g) >= 0.80)));
uretilen gunluk satiri: 900 ornek satirlar 2026-08-02T02:00:00Z uretim dogrulayici[u02] INFO okuma islendi bolge=11 sonuc=gecti sure=210ms 2026-08-02T02:00:01Z uretim okuma-toplayici[u21] INFO okuma islendi bolge=01 sonuc=gecti sure=217ms 2026-08-02T02:15:47Z ortam=uretim servis=dogrulayici ornek=u21 duzey=info olay=okuma alan=05 durum=gecti sure_ms=250 2026-08-02T02:15:52Z ortam=uretim servis=dogrulayici ornek=u08 duzey=info olay=okuma alan=11 durum=gecti sure_ms=83 ayristirma kurali 736/900 satiri esledi; 164 satirda alanlar sessizce bos (%18.2) S7 'hangi bolgenin okumasi dogrulanmadi' gercek bolge 07 29 dusen / 97 okuma ayristirmadan bolge 13 18 dusen / 51 okuma sapma %37.9 yanit ayni mi: hayir kural bolge 07 icin 21 dusen okumayi hic gormedi S5 'gecelik is kac okuma isledi' gercek 900 ham metinde "okuma islendi" gecen satir 736 sapma %18.2 S1 'hangi servis yavasladi' gercek dogrulayici 225 ms ayristirmadan dogrulayici 238 ms yanit ayni mi: evet depo bicimi bayt yanitlanabilen dogru yanit bayt/dogru soru yanlis yanitlanan ham metin 88.590 1/6 0/6 - S5 alanlar + dizin 140.932 6/6 4/6 35.233 S5 S7
Kural 900 satırın 736’sını eşler. Kalan 164 satır reddedilmez; ayrıştırma başarısız olduğunda satır atılmaz, yalnız alanları boş kalır. Depoda 900 kayıt vardır, hepsi aranabilir, hepsinin zaman damgası yerindedir. Yalnız 164’ünde bölge, sonuç, servis ve süre alanları boştur ve bu, bir sorgunun sonucunda hiçbir işaret bırakmaz.
Sessizce Değişen Yanıt
S7 satırı bu dersin en pahalı sayısını taşır. Gerçekte en çok okuma düşen bölge 07’dir: doksan yedi okumanın yirmi dokuzu doğrulamadan geçmemiştir. Ayrıştırılmış alanlardan çıkan yanıt ise 13’tür. Sapma yalnız yüzde 37,9 değildir — yanıt farklıdır.
Nedeni birinci bölümdeki tasarımdadır. 07 bölgesinin düşen okumalarının çoğunu doğrulayıcı yazar; doğrulayıcı yeni biçime geçmiştir; yeni biçim kurala uymaz. Kural, 07 bölgesi için yirmi bir düşen okumayı hiç görmemiştir. 13 bölgesinin düşenlerini ise öteki servisler yazar, onlar eski biçimdedir ve hepsi sayılır. Sonuç, saha ekibinin yanlış bölgeye gitmesidir: iş emirleri 13 bölgesi için açılır, 07 bölgesinin sayaçları okunmamış kalır ve fatura kesilmez.
Bu, ölçüt tarafında görülmeyen bir kayıp türüdür. Bir ölçüt eksikse seri kesilir ve kesinti görülür. Bir alan boşsa sorgu yine çalışır; yalnız daha az satır üzerinde çalışır ve bunu söylemez. Kaybın işareti kaybın kendisiyle birlikte kaybolur.
S5 satırı aynı olguyu daha basit biçimde gösterir. Gecelik işin kaç okuma işlediği sorusu ham
metinde okuma islendi dizesini saymakla yanıtlanabilir — bu, yapılandırılmamış metnin gerçekten
yanıtlayabildiği tek soru türüdür. Ama yeni biçim o dizeyi yazmaz; sayım 900 yerine 736 verir,
yüzde 18,2 eksik. Grep ile alınan sayı da, ayrıştırmadan alınan sayı da aynı nedenle aynı miktarda
yanlıştır.
S1 ise doğru yanıt verir: en yavaş servis her iki hesapta da doğrulayıcıdır. Ama değeri kayar — gerçek ortalama 225, ayrıştırmadan çıkan 238 milisaniye. Aynı eksik veri bir soruda yanıtı değiştirir, ötekinde yalnız sayıyı kaydırır. Hangisinin olacağı verinin dağılımına bağlıdır ve sorgu yazarken bilinmez.
Metin, Alan ve Bedel
Son tablo iki depo biçimini yan yana koyar. Ham metin 88.590 bayttır ve altı sorudan birini yanıtlayabilir — o da yanlış. Alanlar üretildiğinde bayt 140.932’ye çıkar, yüzde elli dokuz artış, ve altı sorunun altısı yanıtlanabilir hâle gelir. Artan şey satırlar değil, alan adları, ayrılmış değerler ve arama dizinidir.
Ama tablonun asıl sütunu üçüncüsüdür: yanıtlanabilen altı sorunun yalnız dördü doğru yanıtlanır. “Yanıtlanabilir” ile “doğru” arasındaki bu iki soruluk boşluk, bu dersin ölçtüğü şeydir. Doğru yanıt başına maliyet 35.233 bayttır; altı sorunun altısı doğru olsaydı 23.489 bayt olurdu. Aradaki fark, kuralın bakımsız kalmasının fiyatıdır ve bunu tetikleyecek bir uyarı yoktur — eşleşmeyen satır oranı bir ölçüt olarak toplanmadıkça yüzde 18,2 hiçbir yerde görünmez.
Kuralı Onarmanın Beş Yolu
Beş ayrıştırma seçeneği aynı satır kümesine uygulanır; eşleşen satır, doğru yanıt ve bayt birlikte ölçülür.
// secenek.mjs — ayristirma seceneklerinin esledigi satir, dogru yanit ve bayt bedeli. import { uret } from "./satir.mjs"; const { satir, kayit } = uret(); const ESKI = /^(\S+) (\w+) ([\w-]+)\[(\w+)\] \w+ .*? bolge=(\d+) sonuc=(\w+) sure=(\d+)ms$/; const YENI = /^(\S+) ortam=(\w+) servis=([\w-]+) ornek=(\w+) .*? alan=(\d+) durum=(\w+) sure_ms=(\d+)$/; const kv = (s) => Object.fromEntries([...s.matchAll(/(\w+)=(\S+)/g)].map((m) => [m[1], m[2]])); const ESLEM = { alan: "bolge", durum: "sonuc", sure_ms: "sure" }; const duzelt = (o) => Object.fromEntries(Object.entries(o).map(([k, v]) => [ESLEM[k] ?? k, v])); const bicimden = (m) => ({ ortam: m[2], servis: m[3], ornek: m[4], bolge: m[5], sonuc: m[6], sure: +m[7] }); const bos = { ortam: "", servis: "", ornek: "", bolge: "", sonuc: "", sure: null }; const SECENEK = [ ["tek kural (eski bicim)", (s) => { const m = ESKI.exec(s); return m ? bicimden(m) : bos; }], ["iki kural (eski + yeni)", (s) => { const m = ESKI.exec(s) || YENI.exec(s); return m ? bicimden(m) : bos; }], ["anahtar-deger, eslemsiz", (s) => ({ ...bos, ...kv(s) })], ["anahtar-deger + alan eslemi", (s) => ({ ...bos, ...duzelt(kv(s)) })], ["kaynakta yapilandirilmis", null], // butun satirlar tek sozlesmeyle yazilir ]; const yapilandirilmis = kayit.map((k) => `zaman=2026-08-02T02:00:00Z ortam=uretim servis=${k.servis}` + ` ornek=${k.ornek} olay=okuma bolge=${k.bolge} sonuc=${k.sonuc} sure=${k.sure}`); const enKotu = (l) => { const t = {}; for (const a of l) { if (!a.bolge) continue; t[a.bolge] ??= [0, 0]; t[a.bolge][0]++; if (a.sonuc === "dustu") t[a.bolge][1]++; } return Object.entries(t).sort((a, b) => b[1][1] - a[1][1])[0]; }; const [gercekBolge] = enKotu(kayit), hamBayt = satir.reduce((t, x) => t + x.length, 0); console.log("secenek eslesen S7 yaniti S7 dogru S5 dogru bayt bayt/dogru soru"); for (const [ad, f] of SECENEK) { const kaynak = f ? satir : yapilandirilmis; const alan = f ? satir.map(f) : yapilandirilmis.map((s) => ({ ...bos, ...kv(s) })); const dolu = alan.filter((a) => a.bolge).length; const [b] = enKotu(alan), s7 = b === gercekBolge, s5 = dolu === kayit.length; const bayt = kaynak.reduce((t, x) => t + x.length, 0) + alan.reduce((t, a) => t + Object.values(a).join("").length + Object.keys(a).length * 2, 0); const dogru = 4 + (s7 ? 1 : 0) + (s5 ? 1 : 0); // S1 S2 S3 S8 her secenekte dogru console.log(`${ad.padEnd(31)}${String(`${dolu}/900`).padStart(8)}${String(b).padStart(11)}` + `${(s7 ? "evet" : "hayir").padStart(10)}${(s5 ? "evet" : "hayir").padStart(10)}` + `${bayt.toLocaleString("tr-TR").padStart(8)}${Math.round(bayt / dogru).toLocaleString("tr-TR").padStart(18)}`); } console.log(`\nham metin ${hamBayt.toLocaleString("tr-TR")} bayt; gercek en kotu bolge ${gercekBolge}`);
secenek eslesen S7 yaniti S7 dogru S5 dogru bayt bayt/dogru soru tek kural (eski bicim) 736/900 13 hayir hayir 121.132 30.283 iki kural (eski + yeni) 900/900 07 evet evet 126.036 21.006 anahtar-deger, eslemsiz 736/900 13 hayir hayir 116.154 29.039 anahtar-deger + alan eslemi 900/900 07 evet evet 115.170 19.195 kaynakta yapilandirilmis 900/900 07 evet evet 161.292 26.882 ham metin 88.590 bayt; gercek en kotu bolge 07
Üçüncü satır beklenmedik olanıdır. Anahtar-değer ayrıştırıcı her iki biçimi de okur ve hiçbir
satırı reddetmez; buna rağmen S7 yanıtı hâlâ 13’tür. Nedeni, ayrıştırmanın başarılı olması ile
alanın doğru adla çıkması arasındaki farktır: yeni biçim bölgeyi alan anahtarıyla yazar,
sorgu bolge alanını arar, ikisi eşleşmez. Biçimden bağımsız bir ayrıştırıcı biçim sorununu
çözer, ad sorununu çözmez.
Dördüncü satır en ucuz doğru seçenektir: aynı ayrıştırıcıya üç satırlık bir alan eşlemi eklenir ve doğru yanıt başına maliyet 30.283 bayttan 19.195 bayta iner. İki kurallı seçenek de doğrudur ama her yeni biçim bir kural daha ister; eşlem tablosu yalnız bir satır büyür.
Beşinci satır sorunu kaynağında çözer: bütün servisler tek bir sözleşmeyle yazar ve ayrıştırma kuralı hiç gerekmez. Kırılmaz, bakım istemez — ve en pahalısıdır: her satırda alan adları yinelendiği için bayt 115.170’ten 161.292’ye, yüzde kırk artar. Ayrıştırma bir bayt tasarrufudur ve tasarrufun karşılığı bakım yüküdür; hangisinin ucuz olduğu satır hacmine ve biçim değişikliği sıklığına bağlıdır.
Özet
- Ayrıştırma kuralı 900 satırın 736’sını eşler; kalan 164 satır atılmaz, yalnız alanları boş kalır (%18,2) ve bu hiçbir sorgu sonucunda işaret bırakmaz.
- Yapılandırılmamış metin altı sorudan yalnız birini yanıtlayabilir (bilinen tek bir dizeyi saymak); alanlar üretilince altısı da yanıtlanabilir hâle gelir, bayt %59 artar.
- Yanıtlanabilir olmak doğru olmak değildir: altı sorunun dördü doğru yanıtlanır. S7’de yanıt bölge 07 yerine bölge 13 çıkar — kural 07 için 21 düşen okumayı hiç görmemiştir.
- Aynı eksik veri S1’de yanıtı değiştirmez, yalnız değeri 225’ten 238 milisaniyeye kaydırır; hangi sorunun bozulacağı verinin dağılımına bağlıdır ve sorgu yazılırken bilinmez.
- Doğru yanıt başına maliyet 35.233 bayttır; kural bakımlı olsaydı 23.489 bayt olurdu. Eşleşmeyen satır oranı bir ölçüt olarak toplanmadıkça %18,2 hiçbir yerde görünmez.
- Biçimden bağımsız ayrıştırıcı biçim sorununu çözer, ad sorununu çözmez (hâlâ 736/900); alan eşlemi eklenince doğru yanıt başına 19.195 bayt olur. Kaynakta yapılandırılmış yazım kırılmaz ama %40 daha fazla bayt tutar.
Sonraki Adım
Buraya kadar ölçüt ve günlük ayrı ayrı toplandı, ayrıştırıldı ve soru kümesine karşı sayıldı. Ama her ölçümde saklama süresi sabit tutuldu ve bedel tek bir ayrıntı düzeyi üzerinden hesaplandı. Gerçek bir hat böyle işlemez: veri yaşlandıkça ayrıntısı azaltılır, seyreltilir, özetlenir ve sonunda atılır. Bir sonraki ders bu basamakları tarar ve her basamakta yanıtlanabilen soru sayısıyla baytı birlikte sayar: soru başına maliyet basamak basamak nereye gider, en ucuz basamak hangi soruyu kaybeder.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.