Yazılarımız

Veri Akademi

PYTHON LOGGİNG: STRUCTURED LOGGİNG, CONTEXT VE İZLENEBİLİRLİK PRATİKLERİ

Loglarınız “ne oldu?” sorusuna cevap veriyor ama “neden oldu?” ve “hangi istekte oldu?” sorularında hep eksik mi kalıyor? Üretimdeki bir hatayı yakaladığınızda, aynı olayın farklı servislerde ve farklı thread’lerde bıraktığı izleri birleştirmek çoğu zaman asıl maliyetli kısım olur.

Bu yazıda structured logging yaklaşımıyla logları arama/filtreleme için uygun hale getirmeyi, context taşıyarak her satırı aynı hikâyenin parçası yapmayı ve izlenebilirlik için trace/correlation ID pratiklerini ele alacağız. Örnekler Python’un yerleşik logging modülü üzerinden ilerleyecek; ihtiyaç duyduğunuz yerde ek kütüphanelere de değineceğiz.

Hedef: Log satırlarını sadece okunabilir değil, aynı zamanda makine tarafından anlamlı hale getirmek; merkezi log toplama araçlarında hızlı sorgulanabilir, uyarı üretilebilir ve metriklere dönüştürülebilir bir yapı kurmak.


Primary Keyword: Python structured logging yaklaşımı neden kritik?

Geleneksel loglama genelde düz metin satırlarından oluşur: “User created”, “Payment failed”, “Timeout”. Bu satırlar gözle okunur ama bir SIEM/ELK/Loki gibi sistemlerde alan bazlı analiz yapmak zorlaşır. Structured logging, log kaydını alanlardan oluşan bir olay olarak ele alır: event, level, timestamp, service, request_id, user_id, latency_ms gibi.

Bu yaklaşım size üç temel kazanım sağlar:

  • Arama/filtreleme hızlanır: “aynı request_id’ye ait tüm satırlar” gibi sorgular doğrudan çalışır.
  • Otomatik uyarı kurmak kolaylaşır: belirli event ve alan kombinasyonlarında alarm üretirsiniz.
  • Gizlilik ve güvenlik kontrol edilebilir: hassas alanları maskeleyerek standartlaştırırsınız.
JSON alanlarıyla düzenlenen log kaydı ve istek kimliğiyle zincirlenen servis akışı

Structured log ile “olay” düşüncesi

Log satırını metin değil, bir olay kaydı olarak düşünmek tasarımı değiştirir. Örneğin “payment failed” yerine; event=payment_failed, reason=insufficient_funds, order_id=..., provider=... gibi alanlar üretirsiniz. Böylece hata türlerini gruplamak, frekans analizleri yapmak ve hangi sağlayıcının daha fazla hata verdiğini görmek mümkün olur.

JSON formatının operasyonel değeri

JSON, yapılandırılmış logların en yaygın taşıma formatıdır. Çoğu log toplayıcı (Fluent Bit, Logstash, Vector) JSON alanlarını otomatik parse edebilir. Bu sayede gözle okumaya değil, alan bazlı sorgulamaya yatırım yaparsınız.


Secondary Keyword: JSON log formatı ve Python logging ile temel kurulum

En basit kurulumda Python’un logging modülünü kullanıp çıktıyı JSON üreten bir formatter ile standartlaştırabilirsiniz. Üç önemli ilke belirleyin:

  • Alan sözlüğü: minimum alanlar (timestamp, level, message/event, logger, service, env).
  • İstisna standardı: hata olduğunda stack trace’i tek bir alanda taşıma.
  • Uygulama kimliği: servis adı/sürümü gibi bağlamı sabitleme.

Örnek: Minimal JSON formatter ile yapılandırma

import json
import logging
import sys
from datetime import datetime, timezone

class JsonFormatter(logging.Formatter):
    def format(self, record: logging.LogRecord) -> str:
        payload = {
            "ts": datetime.now(timezone.utc).isoformat(),
            "level": record.levelname,
            "logger": record.name,
            "event": record.getMessage(),
            "service": "billing-api",
            "env": "prod",
        }
        if record.exc_info:
            payload["exc_info"] = self.formatException(record.exc_info)
        return json.dumps(payload, ensure_ascii=False)

handler = logging.StreamHandler(sys.stdout)
handler.setFormatter(JsonFormatter())

root = logging.getLogger()
root.setLevel(logging.INFO)
root.handlers = [handler]

logging.getLogger("billing").info("payment_initiated")
try:
    1 / 0
except ZeroDivisionError:
    logging.getLogger("billing").exception("payment_failed")

Bu örnek, düz metin yerine temel JSON alanlarını üretir. Bir sonraki adım, her log satırına request düzeyi context eklemek: correlation ID, user ID, tenant gibi.

Alan adlarında tutarlılık

Alan adları büyüdükçe ekipler arası tutarlılık kritik hale gelir. Örneğin request_id mi correlationId mi? Aynı kavram için tek isim seçin. Log sorguları ve dashboard’lar bu kararların üzerine kurulur; isim karmaşası operasyon maliyeti yaratır.


Secondary Keyword: Context propagation ve contextvars ile request bazlı zenginleştirme

Bir web isteği, arka planda onlarca fonksiyon çağrısı ve hatta async task tetikleyebilir. Bu zincirde logların birbirine bağlanması için request context’ini taşımanız gerekir. Python’da bunun için güvenilir bir yol: contextvars. Özellikle async uygulamalarda global değişkenler yerine context-aware değişkenler kullanmanızı sağlar.

Context’i loglara otomatik ekleyen Filter

import logging
import uuid
from contextvars import ContextVar

request_id_var: ContextVar[str] = ContextVar("request_id", default="-")

class ContextFilter(logging.Filter):
    def filter(self, record: logging.LogRecord) -> bool:
        record.request_id = request_id_var.get()
        return True

class SimpleFormatter(logging.Formatter):
    def format(self, record: logging.LogRecord) -> str:
        base = {
            "level": record.levelname,
            "event": record.getMessage(),
            "request_id": getattr(record, "request_id", "-"),
        }
        return str(base)

logger = logging.getLogger("app")
logger.setLevel(logging.INFO)

handler = logging.StreamHandler()
handler.addFilter(ContextFilter())
handler.setFormatter(SimpleFormatter())
logger.handlers = [handler]

def handle_request():
    rid = str(uuid.uuid4())
    token = request_id_var.set(rid)
    try:
        logger.info("request_started")
        logger.info("user_loaded")
        logger.info("request_finished")
    finally:
        request_id_var.reset(token)

handle_request()

Bu yaklaşımda request ID bir kez set edilir, zincirdeki tüm loglara otomatik eklenir. Böylece correlation id ile tek bir isteğe ait tüm olayları kolayca toplayabilirsiniz.

Async uygulamalarda güvenilir taşıma

contextvars, async task’lar arasında context’i doğru şekilde taşır. Bu sayede FastAPI/Starlette gibi async framework’lerde request bazlı alanlar loglara daha güvenilir şekilde eklenir. Thread-local yaklaşımlar ise async dünyada sürprizler doğurabilir.


Secondary Keyword: Trace ID, correlation ID ve distributed tracing ile uyum

Correlation ID genelde uygulama seviyesinde üretilen bir kimliktir. Distributed tracing ise (OpenTelemetry gibi) bir trace ve span kavramı üzerinden tüm servis zincirini modelleyerek daha derin izlenebilirlik sağlar. İdeal durumda loglarınız, tracing sistemindeki trace ID ile eşleşir.

Trace ID’yi loglara eklemek: pratik model

Eğer OpenTelemetry kullanıyorsanız, aktif span içinden trace ID alıp log alanlarına enjekte edebilirsiniz. Kullanmasanız bile, gateway veya API katmanında üretilen bir trace_id alanını downstream servislerde taşımak izlenebilirliği ciddi artırır.

HTTP header standardizasyonu

Servisler arası taşınan kimlikler için tek bir standart belirleyin. Örnek: X-Request-Id veya W3C Trace Context (traceparent). Uygulama içi correlation ID’niz ile tracing ID’nizi karıştırmadan, her ikisini de loglarda alan olarak tutabilirsiniz: request_id ve trace_id.


Secondary Keyword: Log seviyeleri, event isimleri ve hataya odaklı tasarım

Structured logging’de metin yerine “event adı” önem kazanır. Event adlarını fiil + nesne gibi kurgulamak, dashboard ve alert kurulumunu kolaylaştırır: payment_initiated, payment_authorized, payment_failed gibi. Log seviyeleri ise bu event’lerin operasyonel ağırlığını temsil eder.

Seviye stratejisi: INFO gürültüsü, WARN sinyali

INFO seviyesi kritik akış işaretlerini taşıyabilir ama her ayrıntıyı doldurursa gürültü üretir. WARN, “beklenmeyen ama tolere edilebilir” durumları, ERROR ise “aksiyon gerektiren” durumları temsil etmeli. Burada amaç; on-call mühendisinin signal-to-noise oranını yükseltmek.

Exception yakalama: tek satır mı, alan mı?

Stack trace’i tek bir alan olarak taşımak (ör. exc_info) merkezi sistemlerde daha iyi yönetilir. Ayrıca hata sınıfı, hata kodu, dış servis yanıt kodu gibi alanları ayrı tutmak, kök neden analizini hızlandırır. Örnek alanlar: error_type, error_code, upstream_status.


Secondary Keyword: PII maskeleme, güvenlik ve uyumluluk

Loglar sıklıkla farkında olmadan hassas veri taşır: e-posta, telefon, token, kart bilgisi, erişim anahtarı. Structured logging’de avantaj şudur: hassas alanlar tek bir yerde standartlaştığı için maskeleme ve redaksiyon uygulamak kolaylaşır.

Maskeleme kuralları ve allowlist yaklaşımı

En güvenli yaklaşım: “ne loglanabilir?” sorusuna allowlist ile cevap vermek. Yani sadece izin verilen alanları loglamak. Özellikle request/response body loglamak cazip görünse de, üretimde bu pratik hızla risk üretir. Gerekirse özet alanlar üretin: payload_size, item_count, has_discount gibi.

Audit log ile uygulama logunu ayırmak

Uygulama logları gözlem içindir; audit loglar ise uyumluluk ve takip içindir. Aynı yere yığmak yerine ayrıştırın. Audit loglarda kullanıcı aksiyonu, zaman, kaynak, sonuç ve değişen alanlar daha önemlidir; uygulama loglarında performans ve hata detayları öne çıkar.


Secondary Keyword: Observability pipeline, toplama araçları ve performans pratikleri

Structured logging sadece uygulama kodu değil, bir pipeline meselesidir: stdout’a JSON yazarsınız, agent parse eder, merkezi sisteme taşır, indeksler ve sorgulanır. Bu zincirde performans ve maliyet konusu öne çıkar.

Servislerden çıkan alan bazlı kayıtların merkezi sistemde filtrelenip uyarı üretmesine giden akış diyagramı

Sampling ve dinamik ayrıntı seviyesi

Her isteğin her detayını loglamak pahalıdır. Örneğin başarılı isteklerde sampling uygulayıp, hatalı isteklerde ayrıntıyı artırabilirsiniz. Ayrıca “debug modunu” belirli bir request_id için açmak gibi hedefli stratejiler, üretimde veri patlamasını önler.

Latency ve metrik üretimi için alanlar

Loglardan metrik üretmek istiyorsanız ölçü alanlarını standartlaştırın: latency_ms, db_ms, cache_hit, retry_count. Bu alanlar daha sonra dağılım analizi ve SLO takibi için kullanılabilir. Böylece loglar yalnızca metin değil, operasyonel karar veriye dönüşür.


Uygulanabilir bir kontrol listesi

Bu yazıyı kapatmadan önce, ekip içinde hızlıca uygulanabilecek bir kontrol listesi bırakalım. Amaç; her projede aynı temel omurgayı kurmak:

  1. JSON structured logging formatı ve minimum alan sözlüğü tanımlandı mı?
  2. Request bazlı context (request_id, user_id/tenant) otomatik ekleniyor mu?
  3. Trace ID ile uyum hedeflendi mi, header standardı belirlendi mi?
  4. Event adları ve log seviyeleri için ortak sözlük var mı?
  5. PII ve secret maskeleme kuralları pipeline’da veya uygulamada uygulanıyor mu?
  6. Sampling ve performans stratejisi belirlendi mi?

Bu adımların her biri, üretimde “log var ama işe yaramıyor” sorununu azaltır. İyi loglar, hatayı sadece yakalamaz; hızlı teşhis ve kalıcı iyileştirme için veri üretir.


Structured logging ve izlenebilirlik pratiklerini daha geniş örneklerle, framework entegrasyonlarıyla ve gerçek senaryolarla öğrenmek isterseniz şu içeriğe de göz atabilirsiniz: Python eğitimi.

Sonuç olarak; Python’da logging sadece “yazdırma” değildir. Doğru alanlar, doğru context ve doğru standartlarla loglarınız; debugging, operasyon ve güvenlik ekiplerinin ortak dili haline gelir. Üstelik bunun için devasa bir yeniden yazım gerekmeyebilir: küçük ama tutarlı adımlarla loglarınızı izlenebilir bir sisteme dönüştürmek mümkün.

 VERİ AKADEMİ