0
user Programlama Akademi
31-01-2025 5:29 PM
Python

Python 3'te Günlük Kayıt (Logging) Kullanımı

Giriş

logging modülü, standart Python kütüphanesinin bir parçasıdır ve yazılım çalışırken meydana gelen olayları izlemek için bir araç sağlar. Kodunuza günlük kayıt (log) çağrıları ekleyerek, hangi olayların gerçekleştiğini belirtebilirsiniz. Bu modül, bir uygulamanın çalışmasıyla ilgili tanısal günlük kayıtlarını ve bir kullanıcının işlemlerinin analiz amacıyla kaydedildiği denetim günlüklerini (audit logging) destekler. Özellikle olayları bir dosyaya kaydetmek için yaygın olarak kullanılır.

Neden logging Modülünü Kullanmalıyız?

logging modülü, bir programda meydana gelen olayların kaydını tutar ve yazılım çalışırken gerçekleşen olaylarla ilgili çıktıları görmenizi sağlar. Kodunuzda olayların gerçekleşip gerçekleşmediğini kontrol etmek için print() ifadesini kullanmaya alışkın olabilirsiniz. print() ifadesi, kodunuzu hata ayıklamak için temel bir yöntem sağlar. Ancak print() ifadeleriyle kodu takip etmek ve hata ayıklamak uzun vadede logging modülüne göre daha az sürdürülebilirdir. İşte nedenleri: Normal çıktı ile hata ayıklama çıktısını ayırt etmek zorlaşır çünkü ikisi de aynı kanaldan gelir. Kodun farklı yerlerine dağılmış print() ifadelerini devre dışı bırakmak için verimli bir yöntem yoktur. Hata ayıklama tamamlandığında tüm print() ifadelerini kaldırmak zahmetlidir. Anlık olarak tanısal bilgi içeren bir günlük kaydı oluşturulamaz. logging modülünü kullanma alışkanlığı edinmek, özellikle küçük Python betiklerini aşan ve büyüyen uygulamalar için daha uygun bir yöntemdir. Ayrıca, logging modülü uzun vadeli bir hata ayıklama yaklaşımı sağlar. Günlükler, zaman içinde uygulamanızın davranışlarını ve hatalarını gösterebildiği için, geliştirme sürecinde neler olup bittiği hakkında daha iyi bir genel bakış sunar.

Konsola Hata Ayıklama Mesajları Yazdırma

Bilgi: Bu eğitimdeki örnek kodları takip etmek için, yerel sisteminizde bir Python etkileşimli kabuğunu python3 komutunu çalıştırarak açabilirsiniz. Ardından, >>> istemcisine örnek kodları yapıştırabilir veya düzenleyebilirsiniz. Eğer programda gerçekleşen olayları görmek için print() ifadesini kullanmaya alışkınsanız, aşağıdaki gibi bir sınıf tanımlayıp nesneler oluşturan bir programla karşılaşabilirsiniz: pizza.py

class Pizza():
    def __init__(self, ad, fiyat):
        self.ad = ad
        self.fiyat = fiyat
        print("Pizza oluşturuldu: {} ({} TL)".format(self.ad, self.fiyat))

    def yap(self, miktar=1):
        print("{} adet {} pizzası yapıldı.".format(miktar, self.ad))

    def ye(self, miktar=1):
        print("{} adet {} pizzası yendi.".format(miktar, self.ad))


pizza_01 = Pizza("enginar", 15)
pizza_01.yap()
pizza_01.ye()

pizza_02 = Pizza("margherita", 12)
pizza_02.yap(2)
pizza_02.ye()

Yukarıdaki kod, Pizza sınıfının bir nesnesinin adını ve fiyatını tanımlamak için bir __init__ metodu içerir. Ayrıca, biri pizza yapmak (yap()), diğeri pizza yemek (ye()) olmak üzere iki metod barındırır. Bu iki metod, varsayılan olarak 1 değerine sahip bir miktar parametresi alır. Şimdi programı çalıştıralım:

$ python pizza.py

Çıktı şu şekilde olacaktır:

Pizza oluşturuldu: enginar (15 TL)
1 adet enginar pizzası yapıldı.
1 adet enginar pizzası yendi.
Pizza oluşturuldu: margherita (12 TL)
2 adet margherita pizzası yapıldı.
1 adet margherita pizzası yendi.

print() ifadesi sayesinde kodun çalıştığını görebiliriz. Ancak bunun yerine logging modülünü kullanabiliriz.

logging ile Kodun Güncellenmesi

Kodun tamamına dağılmış olan print() ifadelerini kaldırabilir veya yorum satırına alabiliriz. Ardından, dosyanın en üstüne import logging ekleyelim: pizza.py

import logging


class Pizza():
    def __init__(self, ad, fiyat):
        self.ad = ad
        self.fiyat = fiyat
        # print("Pizza oluşturuldu: {} ({} TL)".format(self.ad, self.fiyat))
...

logging modülü varsayılan olarak WARNING seviyesine sahiptir. Bu seviye, DEBUG seviyesinin üzerindedir. Bu örnekte logging modülünü hata ayıklama için kullanacağımızdan, yapılandırmayı değiştirerek logging.DEBUG seviyesindeki bilgilerin konsola yazdırılmasını sağlayabiliriz. Bunu, import ifadesinin altına aşağıdaki satırı ekleyerek yapabiliriz: pizza.py

import logging

logging.basicConfig(level=logging.DEBUG)


class Pizza():
    ...

Bu seviyedeki logging.DEBUG, yukarıdaki kodda bir eşik belirlemek için başvurulan sabit bir tam sayı değerini ifade eder. DEBUG seviyesinin değeri 10'dur. Şimdi, tüm print() ifadelerini logging.debug() ifadeleriyle değiştireceğiz. logging.DEBUG sabit bir değer iken, logging.debug() ise logging modülünün bir yöntemidir. Bu yöntemi kullanırken, print() ifadesine aktarılan aynı dizeyi kullanabiliriz. Aşağıdaki örnekte gösterildiği gibi:

import logging

logging.basicConfig(level=logging.DEBUG)


class Pizza():
    def __init__(self, isim, fiyat):
        self.isim = isim
        self.fiyat = fiyat
        logging.debug("Pizza oluşturuldu: {} (${})".format(self.isim, self.fiyat))

    def yap(self, miktar=1):
        logging.debug("{} adet {} pizzası yapıldı".format(miktar, self.isim))

    def ye(self, miktar=1):
        logging.debug("{} adet {} pizzası yendi".format(miktar, self.isim))


pizza_01 = Pizza("enginar", 15)
pizza_01.yap()
pizza_01.ye()

pizza_02 = Pizza("margherita", 12)
pizza_02.yap(2)
pizza_02.ye()

Bu noktada, programı python pizza.py komutuyla çalıştırdığımızda şu çıktıyı alacağız: Çıktı:

DEBUG:root:Pizza oluşturuldu: enginar ($15)
DEBUG:root:1 adet enginar pizzası yapıldı
DEBUG:root:1 adet enginar pizzası yendi
DEBUG:root:Pizza oluşturuldu: margherita ($12)
DEBUG:root:2 adet margherita pizzası yapıldı
DEBUG:root:1 adet margherita pizzası yendi

Günlük mesajlarında, kök seviyenizi ifade eden root kelimesi ile birlikte ciddi düzey olarak DEBUG seviyesi yer alır. logging modülü, farklı adlara sahip ve farklı çıktılara sahip olacak şekilde birden fazla modülünüz için farklı günlükçüler (loggers) kullanmanıza olanak tanır. Örneğin, farklı isimlere ve çıktılara sahip iki günlükçü şu şekilde ayarlanabilir:

logger1 = logging.getLogger("modul_1")
logger2 = logging.getLogger("modul_2")

logger1.debug("Modül 1 hata ayıklayıcı")
logger2.debug("Modül 2 hata ayıklayıcı")

Çıktı:

DEBUG:modul_1:Modül 1 hata ayıklayıcı
DEBUG:modul_2:Modül 2 hata ayıklayıcı

Şimdi logging modülünü kullanarak mesajları bir dosyaya nasıl yazdıracağımızı öğrenelim.

Mesajları Bir Dosyaya Kaydetmek

logging modülünün birincil amacı, mesajları bir dosyaya kaydetmektir. Mesajları bir dosyada tutmak, zamanla başvurabileceğiniz ve değerlendirilebilecek veriler sağlar; böylece kodunuzda ne gibi değişiklikler yapmanız gerektiğini görebilirsiniz. Mesajları bir dosyaya kaydetmeye başlamak için, logging.basicConfig() yöntemini bir filename parametresi içerecek şekilde değiştirebiliriz. Bu durumda dosya adına test.log diyelim:

import logging

logging.basicConfig(filename="test.log", level=logging.DEBUG)


class Pizza():
    def __init__(self, isim, fiyat):
        self.isim = isim
        self.fiyat = fiyat
        logging.debug("Pizza oluşturuldu: {} (${})".format(self.isim, self.fiyat))

    def yap(self, miktar=1):
        logging.debug("{} adet {} pizzası yapıldı".format(miktar, self.isim))

    def ye(self, miktar=1):
        logging.debug("{} adet {} pizzası yendi".format(miktar, self.isim))


pizza_01 = Pizza("enginar", 15)
pizza_01.yap()
pizza_01.ye()

pizza_02 = Pizza("margherita", 12)
pizza_02.yap(2)
pizza_02.ye()

Yukarıdaki kod, önceki bölümdekiyle aynıdır, ancak artık logların yazdırılacağı dosya adını ekledik. Kodları python pizza.py komutuyla çalıştırdığımızda, dizinimizde test.log adında yeni bir dosya oluşmuş olmalıdır. Bu dosyayı nano (veya tercih ettiğiniz bir metin düzenleyici) ile açalım:

$ nano test.log

Dosya açıldığında aşağıdaki çıktıyı göreceğiz: test.log

DEBUG:root:Pizza oluşturuldu: artichoke ($15)
DEBUG:root:1 adet artichoke pizza yapıldı
DEBUG:root:1 adet pizza yendi
DEBUG:root:Pizza oluşturuldu: margherita ($12)
DEBUG:root:2 adet margherita pizza yapıldı
DEBUG:root:1 adet pizza yendi

Bu, önceki bölümdeki konsol çıktısına benzemektedir, ancak şimdi test.log dosyasına kaydedilmiştir. Dosyayı CTRL + x ile kapatalım ve kodu düzenlemek için tekrar pizza.py dosyasına geçelim. Kodun çoğunu aynı bırakacağız, ancak iki pizza örneği olan pizza_01 ve pizza_02 nesnelerinin parametrelerini değiştireceğiz: pizza.py

import logging

logging.basicConfig(filename="test.log", level=logging.DEBUG)


class Pizza():
    def __init__(self, ad, fiyat):
        self.ad = ad
        self.fiyat = fiyat
        logging.debug("Pizza oluşturuldu: {} (${})".format(self.ad, self.fiyat))

    def yap(self, adet=1):
        logging.debug("{} adet {} pizza yapıldı".format(adet, self.ad))

    def ye(self, adet=1):
        logging.debug("{} adet pizza yendi".format(adet))


# pizza_01 nesnesinin parametrelerini değiştiriyoruz
pizza_01 = Pizza("Sicilian", 18)
pizza_01.yap(5)
pizza_01.ye(4)

# pizza_02 nesnesinin parametrelerini değiştiriyoruz
pizza_02 = Pizza("quattro formaggi", 16)
pizza_02.yap(2)
pizza_02.ye(2)

Bu değişikliklerle kodu python pizza.py komutuyla tekrar çalıştıralım. Kod çalıştıktan sonra test.log dosyasını yeniden açalım:

$ nano test.log

Dosyayı incelediğimizde, programın önceki çalışmasından kalan logların korunduğunu ve yeni satırların eklendiğini göreceğiz: test.log

DEBUG:root:Pizza oluşturuldu: artichoke ($15)
DEBUG:root:1 adet artichoke pizza yapıldı
DEBUG:root:1 adet pizza yendi
DEBUG:root:Pizza oluşturuldu: margherita ($12)
DEBUG:root:2 adet margherita pizza yapıldı
DEBUG:root:1 adet pizza yendi
DEBUG:root:Pizza oluşturuldu: Sicilian ($18)
DEBUG:root:5 adet Sicilian pizza yapıldı
DEBUG:root:4 adet pizza yendi
DEBUG:root:Pizza oluşturuldu: quattro formaggi ($16)
DEBUG:root:2 adet quattro formaggi pizza yapıldı
DEBUG:root:2 adet pizza yendi

Bu bilgiler oldukça faydalıdır, ancak log dosyasını daha bilgilendirici hale getirmek için ek LogRecord (Kayıt) özellikleri ekleyebiliriz. Özellikle, logların oluşturulma zamanını insan tarafından okunabilir bir biçimde eklemek istiyoruz. Bunu, format adlı bir parametreye ekleyerek yapabiliriz. Örneğin: format="%(asctime)s:%(levelname)s:%(message)s"

Bu, zamana ek olarak DEBUG seviyesi ve log mesajını da koruyacaktır. Yeni kod şu şekilde olacaktır: pizza.py

import logging

logging.basicConfig(
    filename="test.log",
    level=logging.DEBUG,
    format="%(asctime)s:%(levelname)s:%(message)s"
)


class Pizza():
    def __init__(self, ad, fiyat):
        self.ad = ad
        self.fiyat = fiyat
        logging.debug("Pizza oluşturuldu: {} (${})".format(self.ad, self.fiyat))

    def yap(self, adet=1):
        logging.debug("{} adet {} pizza yapıldı".format(adet, self.ad))

    def ye(self, adet=1):
        logging.debug("{} adet pizza yendi".format(adet))


pizza_01 = Pizza("Sicilian", 18)
pizza_01.yap(5)
pizza_01.ye(4)

pizza_02 = Pizza("quattro formaggi", 16)
pizza_02.yap(2)
pizza_02.ye(2)

Kodun yukarıdaki eklenen özelliklerle birlikte python pizza.py komutuyla çalıştırılmasıyla, test.log dosyasına insan tarafından okunabilir bir zaman damgası, DEBUG seviyesinin adı ve loglayıcıya iletilen ilgili mesajlar dahil olmak üzere yeni satırlar eklenecektir. test.log

2021-08-19 23:31:34,484:DEBUG:Pizza oluşturuldu: Sicilian ($18)
2021-08-19 23:31:34,484:DEBUG:5 adet Sicilian pizza yapıldı
2021-08-19 23:31:34,484:DEBUG:4 adet pizza yendi
2021-08-19 23:31:34,484:DEBUG:Pizza oluşturuldu: quattro formaggi ($16)
2021-08-19 23:31:34,484:DEBUG:2 adet quattro formaggi pizza yapıldı
2021-08-19 23:31:34,484:DEBUG:2 adet pizza yendi

Bu yöntemle, programınızın zaman içindeki durumunu daha iyi izleyebilir, gerektiğinde sorunları kolayca çözebilirsiniz. Hata ayıklama ve diğer mesajları ayrı dosyalara kaydetmek, Python programınızı zaman içinde bütünsel bir şekilde anlamanızı sağlar. Bu, programa yapılan geçmiş çalışmaların yanı sıra meydana gelen olaylar ve işlemlerle bilgilendirilmiş bir şekilde kodunuzu sorun giderme ve değiştirme fırsatı sunar.

Loglama Seviyeleri Tablosu

Bir geliştirici olarak, loglayıcıda yakalanan bir olaya bir önem seviyesi atayabilirsiniz. Önem seviyeleri aşağıdaki tabloda gösterilmiştir. Loglama seviyeleri teknik olarak tamsayıdır (bir sabit) ve hepsi 10'un katlarıdır. Loglayıcı, başlangıç seviyesi olarak sayısal değeri 0 olan NOTSET ile başlatılır. Ayrıca, tanımlı seviyelere göre kendi seviyelerinizi tanımlayabilirsiniz. Aynı sayısal değeri kullanan bir seviye tanımlarsanız, o değere karşılık gelen adı geçersiz kılarsınız. Aşağıdaki tablo, çeşitli seviye adlarını, sayısal değerlerini, seviyeyi çağırmak için kullanılacak fonksiyonu ve bu seviyenin ne için kullanıldığını gösterir:


Seviye Sayısal Değer Fonksiyon Kullanım Amacı
CRITICAL 50 logging.critical() Ciddi bir hatayı gösterir, program çalışmaya devam edemeyebilir.
ERROR 40 logging.error() Daha ciddi bir sorunu gösterir.
WARNING 30 logging.warning() Beklenmedik bir şey olduğunu ya da olabileceğini belirtir.
INFO 20 logging.info() İşlerin beklendiği gibi çalıştığını onaylar.
DEBUG 10 logging.debug() Sorunları teşhis eder, ayrıntılı bilgi sağlar.


`logging modülü varsayılan olarak seviyeyi WARNING olarak ayarlar, bu nedenle varsayılan olarakWARNING,ERRORveCRITICAL` seviyeleri loglanır. Yukarıdaki örnekte, yapılandırmayı DEBUG seviyesini içerecek şekilde şu kodla değiştirdik:

logging.basicConfig(level=logging.DEBUG)

Komutlar ve hata ayıklayıcıyla çalışma hakkında daha fazla bilgiyi resmi loglama dokümantasyonundan okuyabilirsiniz.

Sonuç

Hata ayıklama, herhangi bir yazılım geliştirme projesinin önemli bir adımıdır. logging modülü Python standart kütüphanesinin bir parçasıdır, yazılım çalışırken meydana gelen olayları izler ve bu olayları ayrı bir log dosyasına kaydedebilir. Bu, zaman içinde programınızı çalıştırmaktan kaynaklanan çeşitli olayları anlamanıza dayalı olarak kodunuzu hata ayıklama fırsatı sunar.


Lisa Tagliaferri tarafından yazılan How To Use Logging in Python 3 Program makalesinin düzenlenmiş çevirisi.


Daha Fazla Oku:


Bu Makaleyi Paylaş

Yorumlar

yorum Yap