Büyük Boyutlu Logların Anlık JSON Parse ve CLI Renklendirme İşlemleri
Devran Baştemur · @DevranBastemur
Can Polat Zorkun · @Polat174
Bedirhan İhtiyar · @acegladd
Umut Açıkgöz · @umutackgoz
Emirhan ÖZGEN · @MESLEKDAA
Modern sistem mimarilerinde, mikroservis kümelerinde ve Güvenlik Operasyon Merkezlerinde (SOC) günlük olarak yüzlerce gigabaytlık log verisi (Zeek ağ kayıtları, Nginx erişim olayları, sistem logları) üretilmektedir. Bu verilerin canlı uçbirimde (CLI - Command Line Interface) incelenmesi ve analiz edilmesi sırasında yapılan en kritik hata, dosyanın tamamını tek seferde belleğe (RAM) yüklemeye çalışmaktır.
Bu çalışmada; yüksek hacimli log dosyalarının bellek darboğazları oluşturmadan akış (streaming) mimarisiyle nasıl işlendiğini, Python, C# ve Go ortamlarında optimize veri okuma modellerini, geliştirilen analiz araçlarının modüler bir Plugin (MainChain) sistemine entegrasyonunu ve uçbirimde (CLI) anlık JSON ayrıştırma ile renklendirme tekniklerini adım adım ele alıyoruz.
1. Zeek (Conn.log) Analiz Sistemlerinde Bellek Yerelliği ve Performans Darboğazları
Siber Güvenlik Operasyon Merkezlerinde (SOC) ve Ağ Tehdit Avcılığı (Threat Hunting) süreçlerinde, Zeek (Bro) gibi Ağ Algılama ve Yanıt (NDR) sistemleri günlük olarak gigabaytlarca conn.log verisi üretir. Bu tür devasa (GB/TB ölçeğinde) metin tabanlı log dosyalarının analizinde seçilen veri işleme mimarisi, sistem kaynaklarının verimliliğini doğrudan etkiler.
İlk aşamada geliştirilen temel log analiz prototipi, dosya işleme mantığını doğrulamakla birlikte büyük dosyalarda şiddetli bellek darboğazına (Memory Bottleneck) ve işlemci yüküne yol açmaktadır.
1.1. readlines() ve Bellek Şişmesi (Memory Bloat)
Klasik dosya okuma yaklaşımında dosyadaki tüm satırlar tek bir hamlede belleğe (RAM) aktarılır:
def read_huge_log(dosya_yolu):
if not os.path.exists(dosya_yolu):
raise FileNotFoundError(f"[!] HATA: {dosya_yolu} bulunamadı!")
with open(dosya_yolu, 'r', encoding='utf-8', errors='ignore') as f:
tum_satirlar = f.readlines() # Tüm satırlar aynı anda RAM'e yüklenir
return tum_satirlar10 GB boyutundaki bir log dosyası, Python'ın dinamik obje işaretçileri (pointers) ve dize (string) yapıları sebebiyle RAM üzerinde yaklaşık 15–20 GB alan işgal eder. Bu durum, RAM yetersizliğinde sistemin SWAP alanına düşmesine ve işletim sistemi seviyesinde Out-Of-Memory (OOM) Killer mekanizmasının tetiklenerek sürecin sonlandırılmasına yol açar.
1.2. String İşlemleri ve Garbage Collection Yükü
Döngü içerisinde uygulanan satir.strip() ve satir.split('\t') işlemleri, her satır için bellek üzerinde yeni metin objeleri ve alt listeler türetir:
for satir in log_listesi:
satir = satir.strip()
if satir.startswith('#'):
continue
sutunlar = satir.split('\t')
if len(sutunlar) > 7:
prot = sutunlar[6]
serv = sutunlar[7]
protokol_sayaci[prot] += 1
if serv != '-':
servis_sayaci[serv] += 1Milyonlarca satırlık bir log kümesinde, her satır için bellek tahsisi (heap allocation) yapılması, CPU döngülerinin önemli bir kısmının bellek temizliğine (Garbage Collection) harcanmasına neden olur.

1.3. Performans Benchmark Projeksiyonu
Aşağıdaki tablo, 10 GB boyutundaki (~30 Milyon satır) sentetik bir conn.log dosyası üzerinde yapılan donanımsal ve teorik projeksiyon karşılaştırmasını göstermektedir:
| Metrik | Mevcut Yöntem (readlines) | Akış Yöntemi (Generator) | Polars / Arrow Mimarisi |
|---|---|---|---|
| RAM Kullanımı | ~18 GB (Ölçeklenemez) | ~50 MB (Sabit) | ~1.5 GB |
| İşlem Süresi | ~120 - 180 Saniye | ~35 - 45 Saniye | ~3 - 6 Saniye |
| I/O Karakteristiği | Ardışık Okuma, Yüksek Heap | Ardışık Akış Okuma | Doğrudan Bellek Eşleme |
| Zaman/Alan Karmaşıklığı | O(N) Alan / O(N) Zaman | O(1) Alan / O(N) Zaman | O(K) Alan / Paralel Zaman |
Bu analiz göstermektedir ki; algoritma doğru çıktılar üretse bile, tüm veriyi aynı anda belleğe yığan mimariler canlı üretim ortamları için sürdürülebilir değildir. Sistemin bellekten bağımsız çalışabilmesi için akış (streaming) modeline geçilmesi zorunludur.
2. Büyük Log Dosyalarını Satır Satır İşlemek: Python, C# ve Go
Bir ağ logu birkaç megabaytken dosyanın tamamını belleğe almak pratik görünebilir. Dosya gigabayt seviyesine ulaştığında ise aynı yöntem gereksiz bellek tüketimine dönüşür. Büyük dosyalarla çalışırken temel mimari ilke basittir: Her kaydı oku, işle ve sıradakine geç.
2.1. Dosyayı Biriktirmeden Okumak (Streaming / Generator)
Python uygulamasında okuma işlemi bir generator üzerinden yürütülür. yield, her çağrıda yalnızca bir satır üretip fonksiyonun kaldığı yerden devam etmesini sağlar. Böylece bütün satırları içeren devasa bir liste oluşturmaya gerek kalmaz:
# -*- coding: utf-8 -*-
import os
import time
from collections import Counter
def read_huge_log(dosya_yolu):
if not os.path.exists(dosya_yolu):
raise FileNotFoundError(f"[!] HATA: {dosya_yolu} bulunamadı!")
# RAM'i şişirmemek için yield generator kullanıyoruz
with open(dosya_yolu, 'r', encoding='utf-8', errors='ignore') as f:
for satir in f:
yield satir.strip()
def ana_fonksiyon():
LOG_FILE = "connf.log"
print("[*] Altay_DEV Log Analiz Sistemi Başlatılıyor...\n")
protokol_sayaci = Counter()
servis_sayaci = Counter()
baslangic = time.perf_counter()
try:
log_stream = read_huge_log(LOG_FILE)
satir_sayaci = 0
for satir in log_stream:
satir_sayaci += 1
if satir.startswith('#'):
continue
sutunlar = satir.split('\t')
if len(sutunlar) > 7:
prot = sutunlar[6]
serv = sutunlar[7]
protokol_sayaci[prot] += 1
if serv != '-':
servis_sayaci[serv] += 1
bitis = time.perf_counter()
gecen_sure = bitis - baslangic
print("=" * 40)
print(" SONUÇLAR")
print("=" * 40)
print(f"[*] Toplam taranan satır : {satir_sayaci:,}")
print(f"[*] İşlem süresi : {gecen_sure:.2f} saniye")
print("\n[+] PROTOKOL DAĞILIMI")
for p, count in protokol_sayaci.most_common():
print(f" -> {p.upper():<5} : {count:,}")
print("\n[+] TOP 5 SERVİS")
for s, count in servis_sayaci.most_common(5):
print(f" -> {s.upper():<5} : {count:,}")
except FileNotFoundError as err:
print(err)
except Exception as e:
print(f"[!] Beklenmedik bir hata oluştu: {e}")
if __name__ == "__main__":
ana_fonksiyon()Buradaki temel kazanç, dosyanın tamamını bellekte tutmamaktır. Yine de "RAM kullanımı hiç artmaz" demek doğru olmaz: Okuma tamponları, işlenen satır ve sayaçlar bellek kullanır. Özellikle farklı kategori sayısı arttıkça sayaçların kapladığı alan da büyür; ancak log dosyası boyutu 1 GB'tan 50 GB'a çıksa dahi anlık bellek kullanımı sabit kalır.
2.2. Aynı Yaklaşım, Üç Farklı Dil
Aynı akış stratejisi farklı dillerde benzer tasarım kalıplarıyla hayata geçirilebilir. Her dil dosyayı sırayla tarar; okuma ve sayma için kendi standart araçlarını kullanır:
| Dil | Satır Okuma Yöntemi | Kategorileri Sayma |
|---|---|---|
| Python | Dosya üzerinde döngü ve yield | collections.Counter |
| C# | StreamReader.ReadLine() ve yield return | Dictionary<string, int> |
| Go | bufio.Scanner ve Scan() | map[string]int |
Go Ortamında Token Sınırı
Go sürümünde uzun satırlara dikkat etmek gerekir. bufio.Scanner varsayılan olarak yaklaşık 64 KiB token sınırı kullanır. Ağ paketlerinin ham yükünü taşıyan daha uzun kayıtlar için scanner.Buffer() ile üst sınırın manuel ayarlanması zorunludur.
2.3. Logdan Hangi Bilgiler Çıkarılıyor?
Kodlar, # ile başlayan başlık satırlarını analiz dışında bırakır ve kayıtları sekme (\t) karakterine göre ayırır. Zeek conn.log düzeninde yedinci sütun protokolü, sekizinci sütun ise servisi temsil eder. Servis alanındaki - değeri doğrudan trafik eşleşmesi bulunmadığını belirttiği için servis istatistiklerine dahil edilmez.
İşlem sonunda taranan toplam satır sayısı, geçen süre, protokol dağılımı ve en sık görülen ilk 5 servis uçbirime (CLI) yazdırılır. Toplam satır sayacı başlıkları ve servisi tanımlanmamış kayıtları da kapsadığı için, TOP 5 servis toplamının genel satır toplamından düşük olması beklenen ve mimariyle uyumlu doğal bir sonuçtur.
3. Geliştirilen Araçların Modüler Plugin (MainChain) Sistemine Entegrasyonu
Geliştirilen bu analiz yeteneklerinin bağımsız birer kütüphane haline getirilerek çalışma zamanında (runtime) ana sisteme dahil edilmesi, mimarinin genişletilebilirliği açısından kritik bir adımdır. Bu aşamada C# (.NET 8) ile geliştirdiğimiz MainChain modüler eklenti altyapısı devreye girmektedir.
3.1. MainChain Plugin Motorunun Mimarisi
MainChain, Açık/Kapalı Prensibi (Open-Closed Principle) temelinde çalışan dinamik bir orkestratördür:
- Kendi dizinindeki
plugins/klasörünü otomatik olarak izler. - Bu klasörün içine atılan her
.dlldosyasını çalışma zamanında .NET Reflection API (Assembly.LoadFrom) ile dinamik olarak okur. - Yüklenen kütüphanedeki tipleri tarayarak
Pluginarayüzünü (interface) uygulayan sınıfları bulur. - Nesne örneğini oluşturur (
Activator.CreateInstance) ve ortak sözleşme olanExecute()metodunu tetikler.
MainChain/
├── MainChain.exe # Ana çalıştırıcı CLI motoru
├── Dockerfile # Konteyner yapılandırması
└── plugins/ # Dinamik modüller klasörü
├── LogAnalyzer2.dll # Akış (streaming) tabanlı optimize eklenti
├── Scapy_Sniffer.dll # Ağ paketlerini dinleyen eklenti
└── LogAnalyzerOld.dll # Karşılaştırma amaçlı geleneksel eklentiKullanıcı sistemi uçbirimden (CLI) basit komutlarla yönetebilir:
.\MainChain.exe -l:plugins/klasöründeki mevcut modülleri numaralarıyla listeler..\MainChain.exe -r 1: İstenen eklentiyi sıra numarasıyla veya dosya adıyla çalıştırır..\MainChain.exe -a <yol>: Dışarıdaki bir DLL dosyasını plugins klasörüne kopyalar..\MainChain.exe -d <ad/no>: Belirtilen eklentiyi siler.
3.2. Visual Studio ile Class Library Projesinin Oluşturulması
Entegrasyonun ilk adımı, eklenti olarak çalışacak kod için doğru proje şablonunu seçmektir. Visual Studio üzerinde konsol uygulaması yerine Class Library (Sınıf Kütüphanesi) şablonu seçilir.

Neden Console App Değil de Class Library?
Bir Console Application, doğrudan işletim sistemi tarafından başlatılabilen bağımsız bir Main metodu içerir ve derlendiğinde .exe üretir.
Class Library ise tek başına çalışamaz; içerisinde Main metodu barındırmaz ve sadece başka programların referans alıp dinamik yükleyebileceği bir .dll dosyası üretir. Ana sistemimiz (MainChain.exe) zaten bağımsız bir konsol programı olduğundan, ona eklenecek parçaların dinamik kütüphane (.dll) formatında olması gerekir.
Projenin devamında, ana sistemle tam uyum sağlaması adına hedef çatı (Target Framework) olarak .NET 8.0 (Long Term Support) seçilerek proje yapılandırılır.
3.3. Plugin İçin Gerekli Kod Yapısı ve Mimari Kurallar
Kodun MainChain tarafından tanınabilmesi için belirli bir mimari standarda uyulması zorunludur:
namespace LogAnalyzer2
{
// 1. Ortak sözleşme arayüzü
public interface Plugin
{
void Execute();
}
// 2. Arayüzü uygulayan eklenti sınıfı
public class DevLogAnalyzer : Plugin
{
// 3. Ana sistemin tetikleyeceği giriş metodu
public void Execute()
{
string logFile = "conn.log";
Console.WriteLine("[*] Altay_DEV Log Analiz Sistemi Başlatılıyor...\n");
if (!File.Exists(logFile))
{
Console.WriteLine($"[!] HATA: {logFile} bulunamadı!");
return;
}
var protokolSayaci = new Dictionary<string, int>();
var servisSayaci = new Dictionary<string, int>();
Stopwatch sw = Stopwatch.StartNew();
int satirSayaci = 0;
try
{
// File.ReadLines -> IEnumerable<string> döndürür, bellek tüketimi O(1) kalır
foreach (string satir in File.ReadLines(logFile))
{
satirSayaci++;
if (satir.StartsWith("#")) continue;
string[] sutunlar = satir.Split('\t');
if (sutunlar.Length > 7)
{
string prot = sutunlar[6];
string serv = sutunlar[7];
if (protokolSayaci.ContainsKey(prot)) protokolSayaci[prot]++;
else protokolSayaci[prot] = 1;
if (serv != "-")
{
if (servisSayaci.ContainsKey(serv)) servisSayaci[serv]++;
else servisSayaci[serv] = 1;
}
}
}
sw.Stop();
Console.WriteLine(new string('=', 40));
Console.WriteLine(" SONUÇLAR");
Console.WriteLine(new string('=', 40));
Console.WriteLine($"[*] Toplam taranan satır : {satirSayaci:N0}");
Console.WriteLine($"[*] İşlem süresi : {sw.Elapsed.TotalSeconds:F2} saniye");
Console.WriteLine("\n[+] PROTOKOL DAĞILIMI");
foreach (var item in protokolSayaci.OrderByDescending(x => x.Value))
{
Console.WriteLine($" -> {item.Key.ToUpper().PadRight(5)} : {item.Value:N0}");
}
Console.WriteLine("\n[+] TOP 5 SERVİS");
foreach (var item in servisSayaci.OrderByDescending(x => x.Value).Take(5))
{
Console.WriteLine($" -> {item.Key.ToUpper().PadRight(5)} : {item.Value:N0}");
}
}
catch (Exception e)
{
Console.WriteLine($"[!] Beklenmedik bir hata oluştu: {e.Message}");
}
}
}
}Bu yapının arkasındaki 4 temel tasarım kararı şunlardır:
public interface PluginNeden Var?
Ana sistem ile eklentiler arasında bir sözleşme (contract) kurar. MainChain, harici DLL içinde hangi sınıfların olduğunu önceden bilemez; ancak arayüz sayesinde o sınıfın parametresiz birExecute()metodu içerdiğini kesin olarak garanti altına alır.- Neden
MainYerineExecuteYazıyoruz?
Bir işletim sistemi sürecinde yalnızca tek birMaingiriş noktası bulunabilir; bu nokta ana orkestratör olanMainChain.exe'ye aittir. Eklentinin kendi başına değil, ana sistem tarafından dışarıdan tetiklenebilmesi içinExecutemetodu kullanılır. - Sınıf Adından Sonra Neden
: PluginEkliyoruz?
Ana sistem Reflection ile DLL'i tararkentype.GetInterfaces()kontrolü yapar. Eğer sınıf bu arayüzü miras almışsa geçerli bir modül olarak sisteme kaydeder. - Neden
.dllKullanıyoruz?
Çıktı.exeolsaydı işletim sisteminde ayrı bir process olarak ayağa kalkar, izole bir bellek havuzu oluşturur ve eklenti olmaktan çıkardı..dllformatı sayesinde kod ana sistemin bellek bağlamında dinamik olarak çalıştırılır.
3.4. Eklentinin Dağıtımı ve Test Edilmesi
Proje geliştirme sürecinde bin/Debug/net8.0/ altında derlenen LogAnalyzer2.dll dosyası MainChain altındaki plugins/ klasörüne kopyalanır.
Üretim ve Performans İçin Release Derlemesi
Geliştirme aşamasında hata ayıklama sembolleri içeren Debug çıktıları kullanılabilir. Ancak kesin performans ölçümlerinde ve canlı üretim ortamlarında, derleyici optimizasyonlarının devrede olduğu Release derlemesi (dotnet build -c Release veya dotnet publish -c Release) tercih edilmelidir.
Uçbirim (CLI) üzerinden eklentiler listelenir:
.\MainChain.exe -l
Eklenti sıra numarasıyla çalıştırılır:
.\MainChain.exe -r 1[+] LogAnalyzer2.dll Başlatılıyor...
[*] Altay_DEV Log Analiz Sistemi Başlatılıyor...
========================================
SONUÇLAR
========================================
[*] Toplam taranan satır : 1,452,890
[*] İşlem süresi : 2.14 saniye
[+] PROTOKOL DAĞILIMI
-> TCP : 1,120,450
-> UDP : 331,890
-> ICMP : 550
[+] TOP 5 SERVİS
-> HTTP : 840,120
-> DNS : 330,500
-> SSL : 198,200
-> SSH : 12,400
-> FTP : 4,200Ölçüm Notu: Yukarıdaki çıktı, yaklaşık 1.45 milyon satırlık gerçek bir Zeek
conn.logdosyası üzerinde alınan canlı CLI çalışma sonucudur. Bölüm 1.3'teki 10 GB (~30 milyon satır) sentetik donanım projeksiyonu ile kıyaslandığında; akış yönteminin 1.45M satırda 2.14 saniye sürmesi, mimarinin pratikteki doğruluğunu kanıtlamaktadır.
3.5. Docker ile Konteynerizasyon
Sistemin taşınabilirliğini sağlamak için hazırlanan Docker altyapısı:
FROM mcr.microsoft.com/dotnet/runtime:8.0
WORKDIR /app
# Ağ paketlerini dinleyen 'Scapy_Sniffer.dll' eklentisinin gereksinimleri (libpcap, scapy)
RUN apt-get update && \
apt-get install -y python3 python3-pip tcpdump libpcap-dev && \
rm -rf /var/lib/apt/lists/*
RUN pip3 install --no-cache-dir scapy --break-system-packages
# Canlı üretim ve optimize performans için Release derlemesi aktarılır
COPY bin/Release/net8.0/ ./
ENTRYPOINT ["dotnet", "MainChain.dll"]Dockerfile Bağımlılık Gerekçesi
LogAnalyzer2.dll saf C# .NET kodundan oluşur ve harici bir kütüphaneye ihtiyaç duymaz. Dockerfile içindeki python3, libpcap-dev ve scapy kurulumları; görselde listelenen ve ham ağ paketlerini koklayıp analiz eden Scapy_Sniffer.dll eklentisinin gereksinimleridir. Salt log analizi yapılacak ortamlarda bu paketler elenerek imaj boyutu önemli ölçüde küçültülebilir.
İmaj derleme ve çalıştırma:
docker build -t altay-main .
docker run -it --rm altay-main -l
docker run -it --rm altay-main -r 14. Anlık JSON Ayrıştırma (Parse) ve Uçbirim (CLI) Renklendirme Mimarisi
Büyük boyutlu logların akış mimarisiyle okunup işlenmesinin ardından, analistin bu verileri insan algısına uygun biçimde hızlıca yorumlayabilmesi için anlık JSON dönüşümü ve uçbirim renklendirme teknikleri uygulanmalıdır.
4.1. Bellek Dostu Log İşleme: Buffer vs. Streaming
| Karşılaştırma Kriteri | Tamponlama / Tam Yükleme (Buffer) | Akış / Satır Satır Okuma (Streaming) |
|---|---|---|
| Bellek (RAM) Tüketimi | Dosya boyutuyla doğru orantılı (Çok Yüksek) | Sabit ve öngörülebilir (~birkaç megabayt) |
| İlk Çıktı Süresi (Latency) | Dosyanın tamamı okunana kadar gecikir | İlk satır geldiği anda çıktı üretilir |
| Canlı Akış Uyumu | tail -f benzeri canlı boru hatlarında çalışmaz | Boru hatlarına (pipe) doğrudan entegre olur |
| Sistem Kararlılığı | Bellek kilitlenmesi ve çökme riski yüksek | Yüksek hacimli veri akışlarında kesintisiz çalışır |
Akış yönteminde dosya; bloklar veya yeni satır (\n) sınırları referans alınarak parça parça işlenir. Bu sayede bellek tüketimi dosya hacminden bağımsız hale getirilerek sabit seviyede tutulur.
4.2. Ham Logların Yapılandırılmış JSON Formatına Dönüştürülmesi
Üretim sistemlerindeki kayıtlar çoğunlukla düz metin (örneğin Nginx Combined formatı) veya satır bazlı JSON (NDJSON / JSONLines) biçimindedir.
Örnek bir Nginx erişim kaydı:
192.168.1.105 - - [21/Apr/2026:14:32:10 +0300] "GET /api/v1/auth HTTP/1.1" 401 128 "-" "Mozilla/5.0"Düz metin logları ayrıştırarak yapılandırılmış JSON nesnesine dönüştürmek, alan bazlı aramayı, SIEM iletimini ve uçbirim üzerinde renk kodlamasını doğrudan mümkün kılar:
{
"timestamp": "2026-04-21T14:32:10+03:00",
"client_ip": "192.168.1.105",
"method": "GET",
"endpoint": "/api/v1/auth",
"status_code": 401,
"response_size": 128
}Devasa log verilerini ayrıştırırken en verimli yaklaşım her satırın bağımsız bir JSON nesnesi olduğu NDJSON (Newline Delimited JSON) biçimidir. Dizi formatındaki klasik JSON belgeleri ([{...}, {...}]) bütün verinin aynı anda belleğe alınmasını şart koştuğundan akış mimarisine uygun değildir.
4.3. Python ile Log Akışını Yakalama ve Renklendirme
Python ortamında akış halindeki veriyi tüketmek, yapılandırılmış sözlüğe (JSON nesnesine) dönüştürmek ve renklendirerek uçbirime basmak için rich kütüphanesinden yararlanılır:
#!/usr/bin/env python3
"""
Büyük Boyutlu Loglar İçin Streaming Parser ve Terminal Renklendirici
"""
import sys
import re
import json
from rich.console import Console
console = Console()
# Nginx Combined Log Deseni
LOG_REGEX = re.compile(
r'^(?P<ip>\S+) \S+ \S+ \[(?P<time>[^\]]+)\] "(?P<method>\S+) (?P<path>\S+) \S+" (?P<status>\d{3}) (?P<size>\S+)'
)
def stream_lines(source):
"""Girdiyi bellek şişmesine yol açmadan satır satır okur."""
for line in source:
clean_line = line.strip()
if clean_line:
yield clean_line
def parse_line(line):
"""Düz metin log satırını ayrıştırarak yapılandırılmış JSON nesnesine dönüştürür."""
match = LOG_REGEX.match(line)
if not match:
return None
data = match.groupdict()
# 4.2'deki standart JSON şemasına eşleme
return {
"timestamp": data["time"],
"client_ip": data["ip"],
"method": data["method"],
"endpoint": data["path"],
"status_code": int(data["status"]),
"response_size": int(data["size"]) if data["size"].isdigit() else 0
}
def get_status_style(status_code):
"""HTTP yanıt koduna göre terminal stili belirler."""
if status_code < 300:
return "bold green"
elif status_code < 400:
return "bold cyan"
elif status_code < 500:
return "bold yellow"
else:
return "bold red"
def main():
try:
# sys.stdin üzerinden kesintisiz veri akışı
for raw_line in stream_lines(sys.stdin):
entry = parse_line(raw_line)
if not entry:
continue
status_style = get_status_style(entry["status_code"])
# Uçbirimde (CLI) formatlı ve renkli satır basımı
# Rich kütüphanesi sys.stdout.isatty() kontrolünü arka planda otomatik yönetir.
console.print(
f"[dim]{entry['timestamp']}[/dim] │ "
f"[cyan]{entry['client_ip']:<15}[/cyan] │ "
f"[magenta]{entry['method']:<6}[/magenta] │ "
f"[{status_style}]{entry['status_code']}[/{status_style}] │ "
f"[yellow]{entry['response_size']:>6}B[/yellow] │ "
f"[white]{entry['endpoint']}[/white]"
)
except KeyboardInterrupt:
console.print("\n[bold red]İşlem kullanıcı tarafından durduruldu.[/bold red]")
if __name__ == "__main__":
main()4.4. Yüksek Hızlı Sistem Yaklaşımı: Go ile Bufio Scanner
Saniyede on binlerce satırın aktığı ve Python'ın CPU darboğazına girebileceği ortamlarda, derlenen ve bellek tahsisini (memory allocation) sıkı kontrol edebilen Go dili tercih edilir.
Aşağıdaki Go uygulaması, NDJSON formatında gelen akışı bufio.Scanner ile tamponlayarak okur; HTTP durum kodlarına göre renk ataması yapar ve standart kütüphane üzerinden terminal kontrolü gerçekleştirerek boru hatlarında temiz çıktı üretir:
package main
import (
"bufio"
"encoding/json"
"fmt"
"os"
)
type LogPayload struct {
Timestamp string `json:"timestamp"`
ClientIP string `json:"client_ip"`
Method string `json:"method"`
Status int `json:"status_code"`
Endpoint string `json:"endpoint"`
ResponseSize int `json:"response_size"`
}
// ANSI Kaçış Dizileri
const (
ColorReset = "\033[0m"
ColorRed = "\033[31m"
ColorGreen = "\033[32m"
ColorYellow = "\033[33m"
ColorCyan = "\033[36m"
)
func formatStatus(code int, useColor bool) string {
if !useColor {
return fmt.Sprintf("%d", code)
}
switch {
case code >= 200 && code < 300:
return fmt.Sprintf("%s%d%s", ColorGreen, code, ColorReset)
case code >= 300 && code < 400:
return fmt.Sprintf("%s%d%s", ColorCyan, code, ColorReset)
case code >= 400 && code < 500:
return fmt.Sprintf("%s%d%s", ColorYellow, code, ColorReset)
case code >= 500:
return fmt.Sprintf("%s%d%s", ColorRed, code, ColorReset)
default:
return fmt.Sprintf("%d", code)
}
}
func main() {
// Uçbirim (TTY) Tespiti: Çıktı bir dosyaya yönlendirildiğinde ANSI kodlarını devre dışı bırak
fileInfo, _ := os.Stdout.Stat()
isTerminal := (fileInfo.Mode() & os.ModeCharDevice) != 0
scanner := bufio.NewScanner(os.Stdin)
// Çok uzun satırlar için tampon belleğin genişletilmesi
buf := make([]byte, 64*1024)
scanner.Buffer(buf, 10*1024*1024) // Maksimum 10MB satır desteği
for scanner.Scan() {
var log LogPayload
rawBytes := scanner.Bytes()
if err := json.Unmarshal(rawBytes, &log); err != nil {
continue // Bozuk JSON satırlarını göz ardı et
}
ipFormatted := log.ClientIP
if isTerminal {
ipFormatted = fmt.Sprintf("%s%s%s", ColorCyan, log.ClientIP, ColorReset)
}
fmt.Printf("[%s] %s -> %s %s [%s] (%dB)\n",
log.Timestamp,
ipFormatted,
log.Method,
log.Endpoint,
formatStatus(log.Status, isTerminal),
log.ResponseSize,
)
}
if err := scanner.Err(); err != nil {
fmt.Fprintf(os.Stderr, "Girdi okuma hatası: %v\n", err)
}
}4.5. Terminal ve CLI Görselleştirme Teknikleri
Büyük veri akışlarında analistin dikkatini kritik olaylara odaklamak için şu görselleştirme kuralları uygulanır:
- ANSI Kaçış Kodları (Escape Codes): Harici bağımlılık olmadan renk kodları (
\033[31m,\033[32m,\033[36mvb.) kullanılarak HTTP durum kodları (2xx başarılı, 3xx yönlendirme, 4xx istemci hatası, 5xx sunucu hatası), hata seviyeleri ve IP adresleri renklendirilir. - TTY ve Boru Hattı Tespiti (
isatty): Komut satırı aracı standart bir uçbirimde çalışırken renk kodları basmalı; çıktısı başka bir dosyaya veya araca yönlendirildiğinde (tool > output.logveyatool | grep) ANSI renk kodlarını otomatik devre dışı bırakmalıdır. Python tarafındaRichkütüphanesisys.stdout.isatty()kontrolünü dahili olarak yürütürken; Go örneğinde standart kütüphaneninos.ModeCharDevicebiti üzerinden uçbirim kontrolü sağlanarak yönlendirmelerde log dosyasının kirlenmesi engellenmiştir. - Filtreleme ve Alarm: Yüksek veri hızında yalnızca şüpheli durumlar (örneğin 401 Unauthorized veya 500 Internal Server Error) kırmızı vurgulanarak analistin reaksiyon süresi hızlandırılır.
Olay Müdahalesi (Incident Response) Açısından Önemi
Brute-force veya Web Shell yükleme gibi saldırı senaryolarında anlık HTTP durum kodlarının (401, 403, 500) renk ayrımlarıyla uçbirime akması, saldırının başladığı anı saniyeler içinde tespit etmeyi mümkün kılar.
Kaynakça
Büyük Boyutlu Logların İşlenmesi ve CLI Renklendirme İçin Kaynakça
- Python Documentation - Memory Management & Generator Expressions
- Microsoft Learn - StreamReader.ReadLine Method
- Go Package Documentation - bufio Scanner Specification
- Microsoft Learn - .NET Class Libraries ve Dynamic Loading Mimarisi
- Microsoft Learn - Reflection in .NET
- Rich Terminal Library Documentation
- ECMA-48 Control Functions for Coded Character Sets (ANSI Standards)
- Newline Delimited JSON (NDJSON) Standard
- Zeek Network Security Monitor - conn.log Specification
- Docker Documentation - .NET Runtime İmajları ile Konteyner Yönetimi

