PYTHON STRUCTURED LOGGING
Sabah 03:42, ödeme servisi hata fırlatıyor. Geliştirici SSH ile sunucuya bağlanıyor, tail -f app.log diyor ve ekranda yüzlerce satır görüyor: "burada", "x=", "geldim mi?", bir traceback, sonra yine "test123". Hangi kullanıcı? Hangi istek? Hangi servis? Bilinmiyor. Çünkü kod, print() ile geliştirilmiş, dosyaya yönlendirilmiş ve production'a öyle çıkmış. Bu yazı, o gecenin bir daha yaşanmaması için Python'da structured logging'i nasıl kuracağınızı anlatıyor.
print() Neden Production'da Yetmez?
print() hızlı bir geliştirme aracıdır — anlık kontrol için iyidir, kalıcı kayıt için değil. Sebepleri sıralarsak.
- Seviye yok: DEBUG, INFO, WARNING, ERROR ayrımı yapılmaz. Her şey eşit gürültüdür.
- Bağlam yok: Zaman damgası, modül adı, process ID, request ID otomatik gelmez.
- Yapısı yok: Düz metindir;
grepile arayabilirsiniz ama analiz edemezsiniz. - Yönlendirme zayıf: stdout'a yazar; dosyaya, syslog'a veya merkezi log toplayıcıya kanal kuramazsınız.
- Performans: Senkron stdout I/O, yüksek trafikte yanıt süresini kötü etkiler.
Standart logging Modülü ile Temel
Python'un kendi logging modülü çoğu ihtiyacı karşılar ve handler, formatter, filter gibi tüm bileşenlere resmi dokümantasyondan ulaşabilirsiniz. Minimum bir kurulum şöyle.
import logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s %(levelname)s %(name)s %(message)s",
handlers=[
logging.FileHandler("app.log"),
logging.StreamHandler(),
],
)
log = logging.getLogger(__name__)
log.info("Sipariş oluşturuldu: id=%s tutar=%s", order_id, total)Bu seviye için yeterli — ama "id=42 tutar=199.90" düz metni, log toplayıcı bir sisteme (Elastic, Loki, CloudWatch) gittiğinde aranabilir alanlar üretmez. Burada yapısal log'a geçmek gerekir.

structlog ile Yapısal Log
structlog kütüphanesi, log satırlarını anahtar-değer çiftleri olarak modeller. Çıktıyı geliştirme ortamında insan-okur renkli, production'da JSON formatında alırsınız.
import structlog
structlog.configure(
processors=[
structlog.contextvars.merge_contextvars,
structlog.processors.add_log_level,
structlog.processors.TimeStamper(fmt="iso"),
structlog.processors.JSONRenderer(),
],
)
log = structlog.get_logger()
log.info("order_created", order_id=42, amount=199.90, user_id=771)Çıktı:
{"event": "order_created", "order_id": 42, "amount": 199.9,
"user_id": 771, "level": "info", "timestamp": "2026-05-24T09:14:02Z"}Artık user_id=771 filtresiyle bir kullanıcının tüm hareketlerini tek sorguyla çekebilirsiniz. Python ekosistemini daha derinden tanımak için kapsamlı Python eğitimi içeriğinden yararlanabilirsiniz.
Request Bağlamı: contextvars
Bir HTTP isteğinin başında request_id üretip log bağlamına bağlarsanız, o istek boyunca yazılan her log satırı bu kimliği taşır. FastAPI / Flask middleware örneği.
import uuid, structlog
from structlog.contextvars import bind_contextvars, clear_contextvars
@app.middleware("http")
async def add_request_id(request, call_next):
clear_contextvars()
bind_contextvars(request_id=str(uuid.uuid4()), path=request.url.path)
response = await call_next(request)
return responseBu noktadan sonra servis katmanında çağırdığınız log.info("payment_failed", reason="insufficient_funds") satırı bile request_id alanını otomatik içerir. Geceleyin debug yaparken tek bir kimlikle isteğin tüm yolculuğunu izlersiniz.
Log Seviyelerini Doğru Kullanmak
- DEBUG: Sadece geliştirme. Değişken değerleri, ara hesaplar.
- INFO: Beklenen iş olayları — "kullanıcı kayıt oldu", "sipariş onaylandı".
- WARNING: Anormal ama kurtarılan durum — "cache miss, DB'ye düştük".
- ERROR: İstek/iş başarısız oldu, kullanıcı etkilendi.
- CRITICAL: Servis çalışmıyor — DB bağlantısı yok, disk dolu.
Production'da varsayılan seviye INFO; DEBUG açılırsa dosya saniyede yüzlerce satır şişer, hem maliyet hem okunabilirlik düşer.
Log Rotasyonu ve Dosya Yönetimi
Tek bir app.log büyüyüp diski doldurur. RotatingFileHandler veya TimedRotatingFileHandler kullanın.
from logging.handlers import TimedRotatingFileHandler
handler = TimedRotatingFileHandler(
"app.log", when="midnight", backupCount=14, encoding="utf-8"
)Production'da daha temiz yaklaşım: uygulamanız sadece stdout'a JSON yazar, log toplayıcı (Fluent Bit, Vector, Promtail) dosya rotasyonu ve gönderimi üstlenir. 12-Factor App felsefesinin önerdiği yol budur.

Hassas Veriyi Loglara Sızdırmamak
Bir log satırı, kullanıcı şifresini ya da kart numarasını içeriyorsa, tüm sisteminizin güvenlik sınıfı log toplayıcıya devredilmiş demektir. Önlem.
- Dict halinde gelen request body'sini doğrudan loglamayın; alan filtresi geçirin.
structlogiçine bir processor ekleyippassword,token,card_numberanahtarlarını***ile maskeleyin.- Exception loglarken
exc_info=Truekullanın ama bağlama sırlı veri eklemeyin. - PII içeren alanlar için hash'leme (kullanıcı izleme için) yeterli olabilir.
Test Edilebilir Logging
Önemli iş olaylarının loglandığını test etmek mümkündür. pytest ile caplog fikstürü, structlog için structlog.testing.capture_logs() kullanılır. Loglar artık sadece "debug çıktısı" değil, doğrulanabilir davranıştır. Hangi olayların loglanması gerektiğini belirlerken Python'un genel ekosistem alışkanlıklarına dair detaylı içerikleri inceleyebilirsiniz.

Sonuç olarak print() bir geliştirme refleksidir; production bir kayıt sistemi ister. logging + structlog + stdout JSON üçlüsü, bir gece yarısı "ne oldu?" sorusunu beş saniyede yanıtlanabilir hale getirir. Bir sonraki deploy'unuza geçmeden önce, kodunuzdaki tüm print çağrılarını arayıp seviyeli, bağlamlı, yapısal log satırlarına dönüştürmek; ödeyeceğiniz en küçük teknik borçtur.



