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.
1. 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.
2. 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.

3. 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.
4. 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.
5. 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.
6. 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.

7. 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.
8. 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.
Sıkça Sorulan Sorular
logging modülü varken neden structlog kullanayım?
Standart logging satır bazlı, metin odaklıdır. structlog ise her log satırını anahtar-değer çiftleri olarak modeller ve JSONRenderer ile doğrudan makine-okunur çıktı verir. Loki, Elasticsearch gibi sistemlerde 'user_id=771' gibi filtrelemeleri tek sorguyla yapabilirsiniz; metin parse etmeye gerek kalmaz.
Production'da DEBUG seviyesini açık bırakmak güvenli mi?
Hayır. DEBUG seviyesi yüksek trafikte saniyede yüzlerce satır üretir; disk alanı, ağ bandı ve log depolama maliyetini şişirir. Ayrıca DEBUG mesajları çoğunlukla iç değişkenler içerir ve yanlışlıkla hassas veri sızdırabilir. Üretimde INFO veya WARNING varsayılan olmalı, DEBUG sadece geçici tanı için açılmalıdır.
Log dosyasını rotate mi etmeliyim yoksa stdout'a mı yazmalıyım?
12-Factor App felsefesine göre uygulama sadece stdout'a yazmalı, rotasyon ve taşımayı çevre yapmalıdır. Docker, systemd veya Kubernetes ortamında stdout JSON çıkışı Fluent Bit veya Vector gibi toplayıcılarla merkezi sisteme akıtılır. Geleneksel sunucu kurulumunda TimedRotatingFileHandler tercih edilebilir.
request_id'yi her log satırına manuel eklemek zorunda mıyım?
Hayır. structlog'un contextvars desteği, middleware'de bir kez bind_contextvars(request_id=...) çağırdığınızda o asenkron görevin altındaki tüm log çağrılarına otomatik olarak alan ekler. İstek bittiğinde clear_contextvars çağırarak sonraki isteklere sızmasını engellersiniz.
Exception traceback'ini nasıl yapısal logluyorum?
structlog'da log.exception('payment_failed', order_id=42) çağrısı veya log.error(..., exc_info=True) kullanımı traceback'i 'exception' anahtarı altında stringe çevirip JSON'a gömer. structlog.processors.format_exc_info processor'ünü zincire eklediğinizde traceback satırları okunabilir biçimde tek alanda toplanır.
Şifre veya kart numarası gibi alanları nasıl maskelerim?
structlog için özel bir processor yazarsınız: event_dict içinde gezerken password, token, authorization, card_number gibi anahtarları '***' ile değiştirir. Bu processor'ü zincirin başına koyduğunuzda, JSONRenderer'a ulaşan veri zaten maskelenmiş olur. Alternatif olarak request body'sini loglamadan önce whitelist filtresinden geçirin.
Logların doğru üretildiğini test edebilir miyim?
Evet. Standart logging için pytest'in caplog fikstürü, structlog için structlog.testing.capture_logs() context manager kullanılır. Test içinde fonksiyonu çağırır, dönen log listesinde beklenen event ve alanların varlığını assert edersiniz. Böylece kritik iş olaylarının loglandığı bir davranışsal sözleşme haline gelir.
JSON loglar geliştirme sırasında okumayı zorlaştırmıyor mu?
structlog bu sorunu ortam bazlı renderer seçimiyle çözer. Geliştirmede ConsoleRenderer(colors=True) okunabilir renkli çıktı verir; production'da JSONRenderer makine-okunur format üretir. Genelde ortam değişkeni (ENV=dev/prod) kontrolüyle configure çağrısında doğru renderer seçilir.


