Takip et

Python’da Gelişmiş Loglama: Uygulamalı Bir Rehber

Uygulamalarınızın derinliklerinde neler olup bittiğini merak mı ediyorsunuz? Bir hata meydana geldiğinde nerede ve neden olduğunu anlamakta zorlanıyor musunuz? İşte bu noktada Python’da loglama devreye giriyor ve kodunuzun kalbine inmenizi sağlayan güçlü bir araç sunuyor. Bu makalede, loglamanın temel prensiplerinden en gelişmiş tekniklerine kadar her şeyi adım adım öğreneceğiz.

Modern yazılım geliştirme dünyasında, herhangi bir uygulamanın kararlılığını ve performansını sağlamak, geliştiriciler için kritik bir görevdir. Bir uygulamanın “gözü” ve “kulağı” niteliğindeki loglama, geliştirme sürecinden üretim ortamına kadar her aşamada vazgeçilmez bir araç haline gelmiştir. Peki, loglama tam olarak nedir ve neden bu kadar önemlidir? Loglama, uygulamanızın belirli olayları (hatalar, uyarılar, bilgi mesajları vb.) kaydetme sürecidir. Bu kayıtlar, sorun giderme, performans analizi, güvenlik denetimi ve hatta kullanıcı davranışlarını anlama gibi birçok farklı amaç için kullanılabilir.

Loglama olmadan, uygulamanızın davranışını anlamak adeta karanlıkta yol almaya benzerdi. Bir hata oluştuğunda, hatanın kaynağını bulmak için saatler harcamanız gerekebilir, çünkü sistemde neler olup bittiğine dair hiçbir kaydınız olmaz. Loglar sayesinde, uygulamanızın belirli bir anda hangi adımları izlediğini, hangi verilerle çalıştığını ve nerede takıldığını net bir şekilde görebilirsiniz. Bu durum, özellikle dağıtık sistemlerde veya karmaşık iş akışlarında hayati önem taşır. Öyleyse, Python’ın zengin

logging

modülünü kullanarak bu süreci nasıl yöneteceğimizi inceleyelim.

Python'ın standart kütüphanesinde yer alan

logging

modülü, oldukça esnek ve güçlü bir loglama altyapısı sunar. Bu modülün temelinde dört ana bileşen bulunur:

  • Loggerlar (Kaydediciler): Log mesajlarını oluşturan ana nesnelerdir. Uygulamanızın farklı bölümleri için farklı loggerlar tanımlayabilirsiniz. Örneğin, veritabanı işlemleri için ayrı, web istekleri için ayrı bir logger kullanmak mümkündür. Her loggerın bir seviyesi vardır ve yalnızca o seviyedeki veya daha yüksek seviyedeki mesajları işler.
  • Handlerlar (İşleyiciler): Loggerlar tarafından oluşturulan log mesajlarını belirli hedeflere yönlendiren nesnelerdir. Bu hedefler bir dosya, konsol, veritabanı, e-posta veya uzak bir sunucu olabilir. Bir loggera birden fazla handler ekleyerek aynı log mesajını farklı yerlere gönderebilirsiniz.
  • Formatlayıcılar (Formatters): Log mesajlarının nasıl görüneceğini belirleyen nesnelerdir. Zaman damgası, log seviyesi, dosya adı, satır numarası gibi bilgileri mesaj metniyle birleştirerek okunabilir bir çıktı oluştururlar.
  • Filtreler (Filters): Log mesajlarını daha fazla kontrol etmek için kullanılırlar. Belirli kriterlere uyan mesajların işlenip işlenmeyeceğine karar verirler. Örneğin, yalnızca belirli bir kelimeyi içeren mesajları loglamak için bir filtre kullanabilirsiniz.

Bu bileşenlerin yanı sıra, log mesajlarının önem derecesini belirten log seviyeleri de kritik bir rol oynar. Python'da varsayılan olarak tanımlanmış beş standart log seviyesi bulunmaktadır:

Seviye Açıklama
DEBUG

Detaylı hata ayıklama bilgileri, yalnızca bir problemi teşhis ederken ilgilenilir.
INFO

Uygulamanın beklendiği gibi çalıştığını doğrulayan genel bilgiler.
WARNING

Beklenmeyen bir olayın veya gelecekte bir soruna yol açabilecek bir durumun göstergesi. Yazılım halen doğru çalışıyor.
ERROR

Ciddi bir problem nedeniyle yazılımın bazı fonksiyonları gerçekleştiremediği durumlar.
CRITICAL

Uygulamanın kendisinin veya kritik bir bileşeninin tamamen durmasına neden olan ciddi hatalar.

Bu seviyeler, log mesajlarının önem sırasına göre filtrelenmesini ve işlenmesini sağlar. Örneğin, üretim ortamında yalnızca

INFO

ve üzeri seviyedeki mesajları loglarken, geliştirme ortamında

DEBUG

seviyesini kullanarak daha detaylı bilgi edinebiliriz. Bu esneklik, Python loglama mekanizmasını son derece kullanışlı kılmaktadır.

Python'da Temel Loglama Nasıl Yapılır? Uygulamalı Adımlar

Python'da loglama işlemine başlamak oldukça kolaydır ve standart

logging

modülü ile gerçekleştirilir. Öncelikle, en temel düzeyde bir loglama kurulumunu nasıl yapacağımızı ele alalım. Varsayılan olarak,

logging

modülü sadece

WARNING

veya daha yüksek seviyedeki mesajları konsola yazdırır. Ancak bu davranışı kolayca değiştirebilir ve ihtiyaçlarımıza göre özelleştirebiliriz. İşte adım adım temel loglama kurulumu ve kullanımı:

Basit Bir Logger Oluşturma ve Kullanma

İlk olarak, Python'ın

logging

modülünü içe aktarıp temel log mesajları göndermeyi deneyelim. Aşağıdaki kod bloğu, çeşitli log seviyelerindeki mesajları nasıl oluşturacağınızı göstermektedir:


import logging

# Varsayılan logger'ı kullanarak log mesajları gönderelim
logging.debug("Bu bir hata ayıklama mesajıdır.")
logging.info("Bu bir bilgi mesajıdır.")
logging.warning("Bu bir uyarı mesajıdır. Dikkat edin!")
logging.error("Bu bir hata mesajıdır. Bir şeyler ters gitti.")
logging.critical("Bu kritik bir hata mesajıdır! Uygulama çökebilir.")
    

Yukarıdaki kodu çalıştırdığınızda, konsolda muhtemelen sadece

WARNING

,

ERROR

ve

CRITICAL

mesajlarını göreceksiniz. Bunun nedeni, Python'ın varsayılan loglama seviyesinin

WARNING

olarak ayarlanmış olmasıdır.

DEBUG

ve

INFO

mesajları varsayılan olarak göz ardı edilir.

Loglama Seviyesini ve Formatını Yapılandırma

Loglama çıktısını tamamen kontrol etmek için

basicConfig()

fonksiyonunu kullanırız. Bu fonksiyon, temel loglama ayarlarını tek bir çağrıyla yapmanızı sağlar. Genellikle betiğinizin veya uygulamanızın başlangıcında bir kez çağrılmalıdır.


import logging

# Temel loglama yapılandırması
# level: Loglanacak en düşük seviye
# format: Log mesajlarının çıktı formatı
# filename: Logların yazılacağı dosya (isteğe bağlı)
logging.basicConfig(level=logging.DEBUG,
                    format='%(asctime)s - %(levelname)s - %(message)s')

logging.debug("Artık DEBUG mesajlarını da göreceksiniz.")
logging.info("Bilgi mesajları artık loglanıyor.")
logging.warning("Hala uyarı mesajları alıyoruz.")
logging.error("Hatalar her zaman önemlidir.")
logging.critical("Kritik durumlar alarm verir.")
    

Bu kodda,

level=logging.DEBUG

diyerek en düşük loglama seviyesini

DEBUG

olarak ayarladık. Bu,

DEBUG

ve daha yüksek tüm seviyelerdeki mesajların işleneceği anlamına gelir.

format

parametresi ise log çıktısının yapısını belirler. Burada kullanılan bazı format belirleyiciler şunlardır:

  • %(asctime)s

    : Log mesajının oluşturulduğu zaman.

  • %(levelname)s

    : Log mesajının seviyesi (DEBUG, INFO, WARNING vb.).

  • %(message)s

    : Asıl log mesajı.

  • %(name)s

    : Logger'ın adı (varsayılan olarak

    root

    ).

  • %(filename)s

    : Loglama çağrısının yapıldığı dosya adı.

  • %(lineno)d

    : Loglama çağrısının yapıldığı satır numarası.

Logları Bir Dosyaya Yazdırma

Sadece konsola log yazmak çoğu zaman yeterli değildir. Kalıcı kayıtlar için logları bir dosyaya yazmamız gerekir.

basicConfig()

fonksiyonuna

filename

parametresini ekleyerek bunu kolayca yapabiliriz:


import logging

# Logları bir dosyaya yönlendirme
logging.basicConfig(level=logging.INFO,
                    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
                    filename='uygulama.log',
                    filemode='a') # 'a' ekleme modudur, 'w' ise her seferinde dosyayı siler

logger = logging.getLogger('ana_uygulama') # Özel bir logger oluşturma

logger.info("Uygulama başlatılıyor...")
logger.debug("Bu mesaj dosyaya yazılmayacak çünkü seviye INFO.") # Bu mesaj loglanmayacak
logger.warning("Veritabanı bağlantısı kurulurken bir sorun yaşandı.")

try:
    1 / 0
except ZeroDivisionError:
    logger.error("Sıfıra bölme hatası meydana geldi!", exc_info=True)
    

Bu örnekte,

filename='uygulama.log'

ile logların

uygulama.log

adlı bir dosyaya yazılmasını sağladık.

filemode='a'

ise, her uygulama çalıştığında logların dosyanın sonuna eklenmesini sağlar. Eğer

'w'

(write) kullanırsak, her seferinde dosya silinip yeniden oluşturulur. Ayrıca,

logging.getLogger('ana_uygulama')

ile özel bir logger oluşturduk. Bu, daha modüler ve yönetilebilir bir loglama yapısı kurmamızı sağlar. Hata durumlarında

exc_info=True

parametresini kullanarak istisna bilgilerini (traceback) loga dahil etmek, sorun giderme açısından son derece değerlidir.

Farklı Handler'ları Birlikte Kullanma

Bazen log mesajlarını hem konsola hem de bir dosyaya yazmak isteyebilirsiniz. Python'ın loglama modülü bunu kolayca yapmamızı sağlar. Farklı

Handler

nesneleri oluşturup bunları aynı

Logger

nesnesine ekleyerek bu işlevi yerine getirebiliriz.


import logging

# Logger oluşturma
logger = logging.getLogger('cift_cikis_logger')
logger.setLevel(logging.DEBUG) # En düşük seviyeyi ayarla

# Konsol handler'ı oluşturma
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO) # Konsola sadece INFO ve üzeri mesajları gönder

# Dosya handler'ı oluşturma
file_handler = logging.FileHandler('cift_cikis.log')
file_handler.setLevel(logging.DEBUG) # Dosyaya tüm DEBUG ve üzeri mesajları gönder

# Formatlayıcı oluşturma
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

# Handler'lara formatlayıcıyı ekleme
console_handler.setFormatter(formatter)
file_handler.setFormatter(formatter)

# Logger'a handler'ları ekleme
logger.addHandler(console_handler)
logger.addHandler(file_handler)

logger.debug("Bu bir hata ayıklama mesajıdır. Sadece dosyaya gider.")
logger.info("Bu bir bilgi mesajıdır. Hem konsola hem dosyaya gider.")
logger.warning("Bu bir uyarı mesajıdır. Hem konsola hem dosyaya gider.")
    

Bu örnekte, aynı logger'a hem bir

StreamHandler

(konsol için) hem de bir

FileHandler

(dosya için) ekledik. Her handler'ın kendi log seviyesi olabilir, bu da çıktıyı daha da özelleştirmemize olanak tanır.

DEBUG

mesajı yalnızca dosyaya yazılırken,

INFO

ve

WARNING

mesajları hem konsola hem de dosyaya yazılacaktır. Bu yapılandırma, uygulamanızın farklı ortamlar veya farklı ihtiyaçlar için esnek loglama stratejileri geliştirmesine imkan tanır.

Gelişmiş Loglama Teknikleri: Performans ve Güvenlik İçin Ne Yapmalıyız?

Temel loglama kurulumu birçok senaryo için yeterli olsa da, büyük ölçekli uygulamalar, yüksek trafikli web servisleri veya uzun süre çalışan arka plan görevleri için daha gelişmiş tekniklere ihtiyaç duyarız. Bu bölümde, loglama performansını artıran, log güvenliğini sağlayan ve log yönetimini kolaylaştıran ileri düzey Python loglama yaklaşımlarını inceleyeceğiz.

Log Dosyası Yönetimi: RotatingFileHandler ve TimedRotatingFileHandler

Tek bir log dosyasına sürekli yazmak, zamanla dosyanın boyutunun aşırı büyümesine neden olabilir. Bu durum, disk alanı sorunlarına yol açabileceği gibi, log dosyasının açılıp okunmasını da zorlaştırabilir. Python'ın

logging.handlers

modülü, bu sorunu çözmek için

RotatingFileHandler

ve

TimedRotatingFileHandler

gibi özel handler'lar sunar.

RotatingFileHandler ile Boyuta Göre Döndürme

RotatingFileHandler

, log dosyasının belirli bir boyuta ulaştığında otomatik olarak yeni bir dosyaya dönmesini sağlar. Eski dosyalar genellikle yeniden adlandırılarak yedeklenir. Bu sayede, log dosyalarınızın kontrol altında kalmasını sağlarsınız.


import logging
from logging.handlers import RotatingFileHandler
import time
import os

LOG_FILE = 'uygulama_dondur.log'

# Önceki log dosyalarını temizleyelim (test amaçlı)
if os.path.exists(LOG_FILE):
    os.remove(LOG_FILE)
for i in range(5):
    if os.path.exists(f"{LOG_FILE}.{i}"):
        os.remove(f"{LOG_FILE}.{i}")

logger = logging.getLogger('rotating_logger')
logger.setLevel(logging.INFO)

# RotatingFileHandler: maxBytes=1024 (1KB), backupCount=5 (5 yedek dosya)
handler = RotatingFileHandler(LOG_FILE, maxBytes=1024, backupCount=5)
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)

print(f"Loglar '{LOG_FILE}' dosyasına yazılacak. Her 1KB'da bir dönecek ve 5 yedek tutulacak.")

# Çok sayıda log mesajı göndererek dosyaların dönmesini sağlayalım
for i in range(1000):
    logger.info(f"Bu {i}. log mesajıdır. Dosya boyutu büyüyor...")
    time.sleep(0.001) # Çok hızlı yazmamak için küçük bir bekleme

print("Loglama tamamlandı. Dosyaları kontrol edin.")
    

Bu örnekte,

uygulama_dondur.log

dosyası her 1 KB'a ulaştığında, mevcut dosya

uygulama_dondur.log.1

olarak yeniden adlandırılır ve yeni bir

uygulama_dondur.log

dosyası oluşturulur.

backupCount=5

sayesinde en fazla 5 eski log dosyası saklanır.

TimedRotatingFileHandler ile Zamana Göre Döndürme

TimedRotatingFileHandler

ise log dosyalarını belirli bir zaman dilimine (örneğin, günlük, haftalık) göre döndürür. Bu, özellikle günlük veya aylık raporlamalar için logları ayrı tutmak istediğinizde çok kullanışlıdır.


import logging
from logging.handlers import TimedRotatingFileHandler
import time
import os

LOG_FILE_TIMED = 'uygulama_zamanli.log'

# Test amaçlı temizlik
if os.path.exists(LOG_FILE_TIMED):
    os.remove(LOG_FILE_TIMED)
# Diğer zamanlanmış dosyaları da temizleyebiliriz, ancak formatı değişkendir.

logger_timed = logging.getLogger('timed_logger')
logger_timed.setLevel(logging.INFO)

# TimedRotatingFileHandler: when='s' (saniye), interval=5 (her 5 saniyede bir), backupCount=3
# 'midnight', 'h', 'd', 'w0'-'w6', 's', 'm' değerleri kullanılabilir.
handler_timed = TimedRotatingFileHandler(LOG_FILE_TIMED,
                                         when='s',
                                         interval=5,
                                         backupCount=3)
formatter_timed = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
handler_timed.setFormatter(formatter_timed)
logger_timed.addHandler(handler_timed)

print(f"Loglar '{LOG_FILE_TIMED}' dosyasına yazılacak. Her 5 saniyede bir dönecek ve 3 yedek tutulacak.")

for i in range(15): # 15 saniye boyunca log gönder (3 döndürme göreceğiz)
    logger_timed.info(f"Bu {i}. zamanlanmış log mesajıdır. Bekleyelim...")
    time.sleep(1)

print("Zamanlanmış loglama tamamlandı. Dosyaları kontrol edin.")
    

Bu örnekte, log dosyası her 5 saniyede bir dönecektir.

backupCount=3

sayesinde 3 eski log dosyası zaman damgasıyla birlikte (

uygulama_zamanli.log.YYYY-MM-DD_HH-MM-SS

formatında) saklanacaktır. Bu, log dosyalarının manuel müdahaleye gerek kalmadan düzenli kalmasını sağlar.

Yapılandırma Dosyası Kullanarak Loglama Ayarları

Uygulamanın kodunda loglama ayarlarını yapmak, karmaşıklaştıkça yönetimi zorlaştırabilir. Daha esnek ve dışarıdan yönetilebilir bir çözüm için loglama yapılandırmasını bir dosyada tutmak tercih edilir. Python'ın

logging.config

modülü, JSON, YAML veya INI formatındaki dosyaları kullanarak loglama ayarlarını yüklemenize olanak tanır.

Örnek bir

logging.ini

dosyası:


[loggers]
keys=root,my_app_logger

[handlers]
keys=consoleHandler,fileHandler

[formatters]
keys=simpleFormatter,detailedFormatter

[logger_root]
level=INFO
handlers=consoleHandler

[logger_my_app_logger]
level=DEBUG
handlers=consoleHandler,fileHandler
qualname=my_app_logger
propagate=0

[handler_consoleHandler]
class=StreamHandler
level=INFO
formatter=simpleFormatter
args=(sys.stdout,)

[handler_fileHandler]
class=FileHandler
level=DEBUG
formatter=detailedFormatter
args=('app_config.log','a')

[formatter_simpleFormatter]
format=%(levelname)s - %(message)s

[formatter_detailedFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(module)s:%(lineno)d - %(message)s
    

Python kodu ile bu dosyayı yükleme:


import logging.config
import os
import sys

# logging.ini dosyasını yükleyelim
# Dosyanın varlığını kontrol etmek önemlidir
config_file = 'logging.ini'
if os.path.exists(config_file):
    logging.config.fileConfig(config_file, disable_existing_loggers=False)
    print(f"Loglama yapılandırması '{config_file}' dosyasından yüklendi.")
else:
    print(f"Hata: '{config_file}' bulunamadı. Varsayılan loglama kullanılıyor.")
    logging.basicConfig(level=logging.INFO) # Yedek olarak basicConfig

# Loggers'ı alalım ve kullanalım
root_logger = logging.getLogger()
app_logger = logging.getLogger('my_app_logger')

root_logger.info("Bu root logger'dan bir bilgi mesajıdır.")
root_logger.debug("Root logger'ın varsayılan seviyesi INFO olduğu için bu görünmez.")

app_logger.debug("Bu özel app_logger'dan bir hata ayıklama mesajıdır. Dosyaya yazılmalı.")
app_logger.info("Bu app_logger'dan bir bilgi mesajıdır. Hem konsola hem dosyaya yazılmalı.")
app_logger.error("Bir hata meydana geldi, app_logger ile loglandı.", exc_info=True)
    

Yapılandırma dosyaları, uygulamanızı dağıtırken veya farklı ortamlar için loglama ayarlarını değiştirmek istediğinizde büyük kolaylık sağlar. Kodda değişiklik yapmadan sadece yapılandırma dosyasını güncelleyerek loglama davranışını değiştirebilirsiniz.

Loglama Güvenliği: Hassas Verilerin Gizlenmesi

Loglama yaparken en önemli konulardan biri de güvenliktir. Kullanıcı şifreleri, kredi kartı numaraları, kişisel kimlik bilgileri gibi hassas verilerin loglara yazılmaması gerekir. Bu tür veriler, log dosyaları ele geçirildiğinde ciddi güvenlik açıkları yaratabilir. Hassas verileri loglardan uzak tutmak için birkaç yöntem mevcuttur:

  1. Filtreler Kullanma:
    logging.Filter

    sınıfını kullanarak log mesajlarını filtreleyebilirsiniz. Belirli anahtar kelimeleri veya desenleri içeren mesajları algılayıp sansürleyebilir veya tamamen atabilirsiniz.

  2. Veri Maskeleme: Loga yazmadan önce hassas verileri maskelemek (örneğin, şifreleri

    ile değiştirmek) yaygın bir uygulamadır.

  3. Doğru Log Seviyeleri: Hassas verileri sadece
    DEBUG

    seviyesinde loglamak ve üretim ortamında bu seviyenin altındaki logları kapatmak bir çözüm olabilir, ancak yine de risk taşır. En güvenli yöntem, bu verileri asla loglamamaktır.


import logging

class CustomFilter(logging.Filter):
    def filter(self, record):
        message = record.getMessage()
        if "şifre" in message or "token" in message:
            record.msg = self.mask_sensitive_data(record.msg)
            if record.args:
                record.args = tuple(self.mask_sensitive_data(str(arg)) if isinstance(arg, str) else arg for arg in record.args)
        return True

    def mask_sensitive_data(self, data):
        # Basit bir maskeleme örneği
        return data.replace("şifre123", "[MASKED_PASSWORD]").replace("my_secret_token", "[MASKED_TOKEN]")

logger = logging.getLogger('security_logger')
logger.setLevel(logging.DEBUG)

# Konsol handler'ı
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
console_handler.setFormatter(formatter)

# Filtreyi handler'a ekleme
console_handler.addFilter(CustomFilter())
logger.addHandler(console_handler)

logger.info("Kullanıcı login oldu: username=testuser, şifre=şifre123")
logger.debug("API çağrısı: endpoint=/api/data, token=my_secret_token")
logger.warning("Genel bir uyarı mesajı.")
    

Bu

CustomFilter

örneği, log mesajında "şifre" veya "token" kelimeleri geçtiğinde ilgili hassas verileri maskeler. Bu tür filtreler, uygulamanızın loglama stratejisini daha güvenli hale getirmenize yardımcı olur. Loglama işlemi sırasında, özellikle kullanıcı girişi veya dış sistemlerle etkileşim içeren kısımlarda dikkatli olmak ve hassas verileri asla açık metin olarak loglamamak temel güvenlik prensibidir.

Gerçek Dünya Senaryoları: Python Loglama Vaka Analizleri

Teorik bilgiler ve basit örnekler loglamanın temellerini anlamak için harikadır, ancak gerçek dünya uygulamalarında loglamanın nasıl kullanıldığını görmek, bu beceriyi derinleştirmek için kritik öneme sahiptir. İşte farklı senaryolarda Python loglamayı nasıl kullanabileceğimize dair iki vaka analizi.

Vaka Analizi 1: E-ticaret Uygulamasında Sipariş İşleme Akışı İzleme

Bir e-ticaret uygulamasında, kullanıcıların sipariş verme, ödeme yapma ve siparişin sevkiyatını takip etme gibi karmaşık bir akış vardır. Bu akışın her adımını izlemek ve olası hataları tespit etmek, müşteri memnuniyeti ve iş sürekliliği için hayati öneme sahiptir. Python loglama modülü bu senaryoda güçlü bir araç olarak karşımıza çıkar.

Senaryo Detayları:

Kullanıcı bir ürün seçer, sepete ekler, ödeme bilgilerini girer ve siparişi tamamlar. Bu süreçte aşağıdaki adımlar gerçekleşir:

  1. Ürün sepete eklenir.
  2. Ödeme işlemi başlatılır.
  3. Ödeme başarılı veya başarısız olur.
  4. Sipariş veritabanına kaydedilir.
  5. Stok güncellemesi yapılır.
  6. Sipariş onay e-postası gönderilir.

import logging
import random
import sys
import os

# Logger'ı yapılandırma
LOG_FILE = 'ecommerce_orders.log'
if os.path.exists(LOG_FILE):
    os.remove(LOG_FILE) # Her çalıştırmada temizle

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler(LOG_FILE),
        logging.StreamHandler(sys.stdout) # Konsola da yaz
    ]
)

order_logger = logging.getLogger('OrderProcessor')

def add_to_cart(user_id, product_id, quantity):
    order_logger.info(f"Kullanıcı {user_id}: Ürün {product_id} (adet: {quantity}) sepete eklendi.")
    # Gerçek sepete ekleme mantığı burada olur
    return True

def process_payment(user_id, order_id, amount):
    order_logger.info(f"Kullanıcı {user_id}: Sipariş {order_id} için {amount} TL ödeme işlemi başlatıldı.")
    if random.random() < 0.9: # %90 başarı oranı
        order_logger.info(f"Kullanıcı {user_id}: Sipariş {order_id} için ödeme başarılı.")
        return True
    else:
        order_logger.error(f"Kullanıcı {user_id}: Sipariş {order_id} için ödeme başarısız oldu!")
        return False

def save_order_to_db(order_details):
    order_logger.info(f"Sipariş {order_details['order_id']} veritabanına kaydediliyor.")
    if random.random() < 0.95: # %95 başarı oranı
        order_logger.info(f"Sipariş {order_details['order_id']} başarıyla kaydedildi.")
        return True
    else:
        order_logger.critical(f"Sipariş {order_details['order_id']} veritabanına kaydedilemedi! Kritik hata.", exc_info=True)
        return False

def update_stock(product_id, quantity):
    order_logger.info(f"Ürün {product_id} için stok güncelleniyor, {quantity} adet azaltılıyor.")
    # Stok güncelleme mantığı
    return True

def send_confirmation_email(user_id, order_id):
    order_logger.info(f"Kullanıcı {user_id} için sipariş {order_id} onay e-postası gönderiliyor.")
    # E-posta gönderme mantığı
    return True

def complete_order_flow(user_id, product_id, quantity, amount):
    order_id = f"ORD-{random.randint(1000, 9999)}"
    order_logger.info(f"[{order_id}] Yeni sipariş akışı başlatıldı: Kullanıcı={user_id}, Ürün={product_id}, Adet={quantity}, Tutar={amount}")

    if not add_to_cart(user_id, product_id, quantity):
        order_logger.error(f"[{order_id}] Sepete eklenemedi. Akış durduruldu.")
        return

    if not process_payment(user_id, order_id, amount):
        order_logger.error(f"[{order_id}] Ödeme başarısız. Akış durduruldu.")
        return

    order_details = {
        'order_id': order_id,
        'user_id': user_id,
        'product_id': product_id,
        'quantity': quantity,
        'amount': amount,
        'status': 'completed'
    }
    if not save_order_to_db(order_details):
        order_logger.critical(f"[{order_id}] Veritabanı kaydı başarısız. Akış kritik hata ile durduruldu.")
        return

    update_stock(product_id, quantity)
    send_confirmation_email(user_id, order_id)

    order_logger.info(f"[{order_id}] Sipariş başarıyla tamamlandı.")

# Birkaç sipariş akışını simüle edelim
print("\n--- Sipariş Akışı 1 ---")
complete_order_flow("user_alice", "PROD-101", 2, 150.00)
print("\n--- Sipariş Akışı 2 ---")
complete_order_flow("user_bob", "PROD-203", 1, 75.50)
print("\n--- Sipariş Akışı 3 (Hata Bekleniyor) ---")
complete_order_flow("user_charlie", "PROD-404", 3, 220.00)
    

Bu örnekte, her bir sipariş akışı adımı için

INFO

seviyesinde loglar oluşturulur. Olası hatalar (

payment_failed

,

db_save_failed

)

ERROR

veya

CRITICAL

seviyesinde loglanarak hemen fark edilmeleri sağlanır.

exc_info=True

kullanımı, istisna ayrıntılarını da loga ekleyerek hata ayıklamayı kolaylaştırır. Bu sayede, operasyon ekipleri anormallikleri hızlıca tespit edebilir ve müşteri deneyimi olumsuz etkilenmeden müdahale edebilir.

Vaka Analizi 2: Veri İşleme Hattında Performans ve Anomali Tespiti

Büyük veri işleme hatlarında (data pipelines), verilerin farklı aşamalardan geçerek dönüştürüldüğü ve analiz edildiği karmaşık süreçler bulunur. Bu hatların performansı ve doğru çalışması, nihai analitik sonuçların güvenilirliği için kritik öneme sahiptir. Loglama, bu hatlardaki darboğazları ve veri anormalliklerini tespit etmek için mükemmel bir araçtır.

Senaryo Detayları:

Bir veri hattı, ham verileri alır, temizler, dönüştürür ve bir veri ambarına yükler. Her adımın ne kadar sürdüğünü ve potansiyel veri kalitesi sorunlarını izlemek istiyoruz.


import logging
import time
import random
import sys
import os

# Logger'ı yapılandırma
LOG_FILE_DATA_PIPELINE = 'data_pipeline.log'
if os.path.exists(LOG_FILE_DATA_PIPELINE):
    os.remove(LOG_FILE_DATA_PIPELINE)

logging.basicConfig(
    level=logging.DEBUG, # Gelişme ortamında daha detaylı loglar için DEBUG
    format='%(asctime)s - %(name)s - %(levelname)s - %(process)d - %(message)s',
    handlers=[
        logging.FileHandler(LOG_FILE_DATA_PIPELINE),
        logging.StreamHandler(sys.stdout)
    ]
)

pipeline_logger = logging.getLogger('DataPipeline')

def fetch_data(source_id):
    start_time = time.time()
    pipeline_logger.debug(f"[{source_id}] Veri kaynağı {source_id}'dan veri çekiliyor...")
    time.sleep(random.uniform(0.1, 0.5)) # Simüle edilmiş gecikme
    data_records = random.randint(100, 1000)
    end_time = time.time()
    pipeline_logger.info(f"[{source_id}] Veri çekme tamamlandı ({data_records} kayıt). Süre: {end_time - start_time:.2f}s")
    return {"source": source_id, "records": data_records, "raw_data": [random.randint(1, 100) for _ in range(data_records)]}

def clean_data(data):
    start_time = time.time()
    source_id = data['source']
    total_records = data['records']
    corrupted_records = random.randint(0, int(total_records * 0.05)) # %0-5 arası bozuk kayıt
    cleaned_records = total_records - corrupted_records
    
    if corrupted_records > 0:
        pipeline_logger.warning(f"[{source_id}] {corrupted_records} adet bozuk kayıt tespit edildi ve temizlendi.")
    
    time.sleep(random.uniform(0.05, 0.3))
    end_time = time.time()
    pipeline_logger.info(f"[{source_id}] Veri temizleme tamamlandı ({cleaned_records} geçerli kayıt). Süre: {end_time - start_time:.2f}s")
    data["cleaned_records"] = cleaned_records
    return data

def transform_data(data):
    start_time = time.time()
    source_id = data['source']
    pipeline_logger.debug(f"[{source_id}] Veriler dönüştürülüyor...")
    # Dönüşüm mantığı (örneğin,聚合, zenginleştirme)
    if random.random() < 0.02: # %2 hata olasılığı
        raise ValueError("Veri dönüşüm hatası!")
    
    time.sleep(random.uniform(0.1, 0.6))
    end_time = time.time()
    pipeline_logger.info(f"[{source_id}] Veri dönüşümü tamamlandı. Süre: {end_time - start_time:.2f}s")
    data["transformed"] = True
    return data

def load_data_to_warehouse(data):
    start_time = time.time()
    source_id = data['source']
    pipeline_logger.debug(f"[{source_id}] Veri ambarına yükleniyor...")
    time.sleep(random.uniform(0.2, 0.8))
    end_time = time.time()
    pipeline_logger.info(f"[{source_id}] Veri ambarına yükleme tamamlandı. Yüklenen kayıt: {data.get('cleaned_records', 'N/A')}. Süre: {end_time - start_time:.2f}s")
    return True

def run_data_pipeline(source_id):
    pipeline_logger.info(f"--- Veri hattı başlatıldı: Kaynak {source_id} ---")
    try:
        raw_data = fetch_data(source_id)
        cleaned_data = clean_data(raw_data)
        transformed_data = transform_data(cleaned_data)
        load_data_to_warehouse(transformed_data)
        pipeline_logger.info(f"--- Veri hattı başarıyla tamamlandı: Kaynak {source_id} ---")
    except ValueError as e:
        pipeline_logger.error(f"--- Veri hattı hatası: Kaynak {source_id}. Hata: {e} ---", exc_info=True)
    except Exception as e:
        pipeline_logger.critical(f"--- Veri hattı kritik hatası: Kaynak {source_id}. Beklenmeyen hata: {e} ---", exc_info=True)

# Çeşitli veri kaynakları için hattı çalıştıralım
for i in range(1, 4):
    run_data_pipeline(f"SRC-{i}")
    time.sleep(1) # Arka arkaya çok hızlı çalıştırmamak için
    

Bu senaryoda, her bir işlem adımının başlangıç ve bitiş zamanlarını

INFO

seviyesinde loglayarak performans izlemesi yaparız.

DEBUG

mesajları, geliştirme aşamasında daha detaylı içgörüler sağlar. Veri temizleme adımında tespit edilen bozuk kayıtlar

WARNING

olarak işaretlenir.

transform_data

fonksiyonundaki olası hatalar

ERROR

olarak yakalanır ve

exc_info=True

ile detaylı hata bilgisi loglanır. Bu sayede, hem hattın genel performansını gözlemleyebilir hem de olası veri kalitesi sorunlarını veya işlem hatalarını proaktif olarak tespit edebiliriz. Zaman damgaları ve süreç ID'leri sayesinde, logları merkezi bir log toplama sisteminde (ELK Stack gibi) analiz ederken belirli bir kaynak veya sürecin davranışını kolayca izleyebiliriz.

Loglama İpuçları ve En İyi Uygulamalar

Python'da loglama sadece temel fonksiyonları kullanmaktan ibaret değildir. Uygulamanız büyüdükçe ve karmaşıklaştıkça, loglama stratejinizin de gelişmesi gerekir. İşte daha verimli, okunabilir ve yönetilebilir loglar oluşturmanıza yardımcı olacak bazı ipuçları ve en iyi uygulamalar:

1. Anlamlı Logger İsimleri Kullanın

Her modül veya önemli uygulama bileşeni için ayrı bir logger oluşturmak, logların izlenebilirliğini artırır. Örneğin, bir web uygulamasında

auth

modülü için

logging.getLogger('my_app.auth')

, veritabanı işlemleri için

logging.getLogger('my_app.database')

gibi hiyerarşik isimler kullanın. Bu, logları filtrelerken veya analiz ederken hangi mesajın nereden geldiğini hızlıca anlamanızı sağlar.


import logging

# Hiyerarşik logger örnekleri
logger_auth = logging.getLogger('my_app.auth')
logger_db = logging.getLogger('my_app.database')

# Her logger kendi içinde yapılandırılabilir
logger_auth.setLevel(logging.INFO)
logger_db.setLevel(logging.DEBUG) # Veritabanı için daha detaylı log

# ... handler ve formatter ekleme işlemleri ...

logger_auth.info("Kullanıcı 'admin' başarıyla giriş yaptı.")
logger_db.debug("Veritabanı bağlantısı kontrol ediliyor...")
    

2. İstisnaları Doğru Şekilde Loglayın (
exc_info=True

)

Bir hata (istisna) meydana geldiğinde, yalnızca hata mesajını değil, tüm izleme bilgisini (traceback) loglamak çok önemlidir.

logger.error()

veya

logger.exception()

yöntemleriyle

exc_info=True

parametresini kullanarak bunu yapabilirsiniz.

logger.exception()

özel bir durumdur, her zaman

ERROR

seviyesinde loglar ve otomatik olarak

exc_info=True

olarak ayarlanmıştır.


import logging

logging.basicConfig(level=logging.ERROR, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger('error_logger')

try:
    result = 10 / 0
except ZeroDivisionError as e:
    logger.error("Sıfıra bölme hatası meydana geldi!", exc_info=True)
    # veya daha kısa olarak:
    # logger.exception("Sıfıra bölme hatası meydana geldi!")
    

3. Dinamik ve Bağlamsal Loglama (Contextual Logging)

Log mesajlarına, uygulamanın o anki durumu hakkında daha fazla bilgi eklemek, sorun gidermeyi kolaylaştırır. Örneğin, bir web isteği işlenirken, isteğin benzersiz ID'sini, kullanıcının IP adresini veya kullanıcının ID'sini her log mesajına dahil etmek faydalı olabilir. Bunu yapmanın bir yolu, log mesajlarını formatlarken veya

LoggerAdapter

kullanarak ek bağlam bilgileri eklemektir.


import logging

# LoggerAdapter ile bağlamsal bilgi ekleme
class ContextualAdapter(logging.LoggerAdapter):
    def process(self, msg, kwargs):
        return f"[{self.extra['request_id']}] {msg}", kwargs

logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
base_logger = logging.getLogger('my_api')

request_id = "req-12345"
adapter = ContextualAdapter(base_logger, {'request_id': request_id})

adapter.info("API isteği başlatıldı.")
# ... işleme ...
adapter.warning("Bazı veriler eksik.")
adapter.info("API isteği tamamlandı.")
    

Bu yaklaşım sayesinde, farklı isteklerin loglarını kolayca ayırt edebilir ve izleyebiliriz. Ayrıca her log çağrısında tekrar tekrar aynı bilgileri yazmak zorunda kalmayız.

4. Log Seviyelerini Akıllıca Kullanın

Her log mesajının uygun bir seviyede olması gerekir. Gereksiz yere yüksek seviyelerde loglama (örneğin her şeyi

ERROR

olarak işaretlemek) gerçek sorunların gözden kaçmasına neden olabilir. Aşırı

DEBUG

loglama ise performans düşüşüne ve log dosyalarının aşırı büyümesine yol açar.

  • DEBUG

    : Geliştirme ve hata ayıklama için çok detaylı bilgiler.

  • INFO

    : Uygulamanın normal işleyişini gösteren genel bilgiler (örneğin, "Kullanıcı giriş yaptı", "Veri işleme başladı").

  • WARNING

    : Potansiyel sorunlar veya beklenen ama istenmeyen durumlar (örneğin, "Yapılandırma dosyası bulunamadı, varsayılanlar kullanılıyor").

  • ERROR

    : Uygulamanın belirli bir fonksiyonunun düzgün çalışmasını engelleyen hatalar.

  • CRITICAL

    : Uygulamanın veya sistemin kararlılığını etkileyen ciddi hatalar.

5. Harici Kütüphaneler ve Merkezi Loglama Sistemleri ile Entegrasyon

Büyük ölçekli uygulamalarda, logları merkezi bir sistemde toplamak ve analiz etmek esastır. Popüler seçenekler arasında ELK Stack (Elasticsearch, Logstash, Kibana), Splunk, Graylog ve Datadog bulunur. Python'da, bu sistemlere log göndermek için çeşitli kütüphaneler (örneğin,

logstash-formatter

veya

fluent-logger

) veya doğrudan TCP/UDP socket handler'ları kullanılabilir.

Merkezi loglama, tüm uygulama ve servislerinizin loglarını tek bir yerden izlemenizi, aramanızı ve görselleştirmenizi sağlar. Bu, mikroservis mimarilerinde veya birden fazla sunucuya dağılmış uygulamalarda sorun gidermeyi ve operasyonel görünürlüğü önemli ölçüde artırır.

Bu ipuçları ve en iyi uygulamaları takip ederek, Python uygulamalarınızda daha sağlam, izlenebilir ve yönetilebilir bir loglama stratejisi oluşturabilirsiniz. Loglama, sadece hata ayıklama aracı değil, aynı zamanda uygulamanızın sağlığını ve performansını sürekli izlemek için güçlü bir operasyonel araçtır.

Sonuç ve Sıkça Sorulan Sorular

Python'da loglama, uygulamanızın davranışını anlamak, hataları tespit etmek, performansı izlemek ve güvenlik açıklarını saptamak için hayati bir araçtır. Bu makalede, loglamanın temel kavramlarından başlayarak, farklı log seviyeleri, handler'lar, formatlayıcılar ve filtreler gibi yapı taşlarını detaylı bir şekilde inceledik. Ayrıca, log dosyası yönetimi için

RotatingFileHandler

ve

TimedRotatingFileHandler

gibi gelişmiş tekniklere değindik ve yapılandırma dosyaları kullanarak loglama ayarlarını nasıl yöneteceğimizi öğrendik. Gerçek dünya vaka analizleri sayesinde, e-ticaret sipariş işleme ve veri işleme hatları gibi karmaşık senaryolarda loglamanın pratik uygulamalarını gördük.

Unutmayın ki etkili bir loglama stratejisi, sadece kodunuzdaki hataları bulmaktan çok daha fazlasını sunar. Uygulamanızın yaşam döngüsü boyunca bir "kara kutu" olmasını engelleyerek, size sürekli bir geri bildirim mekanizması sağlar. İyi yapılandırılmış loglar, geliştirme süreçlerini hızlandırır, operasyonel yükü azaltır ve nihayetinde daha güvenilir ve bakımı kolay yazılımlar oluşturmanıza yardımcı olur. Artık Python'daki bu güçlü loglama yeteneklerini kendi projelerinizde kullanarak uygulamalarınızın derinliklerine inmeye hazırsınız.

Sıkça Sorulan Sorular (SSS)

1. Python'da loglama yapmak performansımı etkiler mi?

Evet, her log mesajı bir miktar CPU ve I/O kaynağı tüketir. Özellikle çok detaylı (

DEBUG

seviyesi) loglama veya yüksek yoğunluklu I/O işlemleri gerektiren handler'lar (örneğin ağ üzerinden log gönderme) performans üzerinde gözle görülür bir etki yaratabilir. Bu nedenle üretim ortamlarında log seviyelerini dikkatli ayarlamak ve gerektiğinde asenkron loglama (

QueueHandler

) kullanmak önemlidir.

2. Hangi log seviyesini ne zaman kullanmalıyım?

  • DEBUG

    : Geliştirme ve hata ayıklama aşamasında, her detay adımını izlemek istediğinizde.

  • INFO

    : Uygulamanın normal akışını, önemli olayları (kullanıcı girişi, işlem başlatma) kaydetmek için.

  • WARNING

    : Beklenmeyen ama uygulamanın çalışmasını engellemeyen durumlar (yapılandırma hatası, deprecated özellik kullanımı).

  • ERROR

    : Uygulamanın bir kısmının işlevini yerine getiremediği durumlar (veritabanı bağlantı hatası, dosya okuma hatası).

  • CRITICAL

    : Uygulamanın veya sistemin tamamen çökmesine neden olan çok ciddi, kurtarılamaz hatalar.

3. Python loglama modülü hassas verileri otomatik olarak gizler mi?

Hayır, Python'ın standart

logging

modülü hassas verileri otomatik olarak gizlemez. Bu, geliştiricinin sorumluluğundadır. Makalede bahsedilen filtreler veya manuel maskeleme yöntemleri kullanılarak hassas verilerin loglara sızması önlenmelidir. Güvenlik ve gizlilik her zaman öncelikli olmalıdır.

4. Log dosyalarını manuel olarak silmek yerine daha iyi bir yöntem var mı?

Kesinlikle! Makalede de değindiğimiz gibi,

RotatingFileHandler

ve

TimedRotatingFileHandler

gibi handler'lar, log dosyalarını belirli boyut veya zamana göre otomatik olarak döndürür ve eski dosyaları siler. Bu, log dosyalarınızın disk alanını doldurmasını engeller ve manuel müdahaleye gerek kalmadan düzenli kalmasını sağlar. Ayrıca, merkezi loglama sistemleri genellikle logları belirli bir süre sonra otomatik olarak arşivler veya siler.

5. Birden fazla Python uygulaması aynı log dosyasına yazabilir mi?

Evet, yazabilirler ancak dikkatli olunmalıdır. Farklı uygulamalar aynı

FileHandler

ile aynı dosyaya yazarsa, eşzamanlı erişim sorunları yaşanabilir ve log mesajları karışabilir. Çözüm olarak, her uygulama için ayrı bir log dosyası kullanmak veya

logging.handlers.WatchedFileHandler

gibi kilit mekanizmaları olan handler'lar kullanmak ya da en iyisi, logları merkezi bir log toplama sistemine (örneğin Syslog veya ELK Stack) göndermek daha güvenli ve yönetilebilirdir.

Yorumlar
İçeriği beğendiniz mi? Bir tartışma başlatın veya görüşlerinizi paylaşın.
Yorum Yaz

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Gönder

E-posta Bülteni
Yazılım Topluluğuna Katılın
En son güncellemeleri, yaratıcı ipuçlarını ve özel kaynakları doğrudan e-posta kutunuza alın. Tasarım ve inovasyonun geleceğini birlikte keşfedelim.
Exit mobile version