Loglama

Gökhan YAVAŞ
gokhanyavas
Published in
4 min readJan 4, 2017

Loglama, bir sistemdeki hareketliliği kaydetmek için kullanılan yapıdır. Python standart kütüphanesi içinde loglama için çok güçlü bir kütüphane barındırır. Bu kütüphane ile geliştirdiğimiz programlarda hata ayıklamak aynı zamanda ifadeleri yazdırmak için loglama kullanabiliriz. Bu yazıda aşağıdaki bölümleri inceleyeceğiz:

  1. Basit log oluşturma,
  2. Birden çok modül kullanarak loglama,
  3. Logları Biçimlendirme,
  4. Logları yapılandırma.

Bu yazının sonunda uygulamalarınızda kendi loglarınızı yazabilecek seviyede olacaksınız. Hazırsanız başlayalım:

Basit Log Oluşturma

Adındanda anlaşılacağı üzere çok basit bir şekilde loglama işlemi yapabiliriz. Bunun için logging modülünü kullanırız.

import logginglogging.basicConfig(filename="kayitlar.log", level=logging.INFO)logging.debug("Bu debug mesajidir.")
logging.info("Bilgi mesajidir")
logging.error("Hata mesajidir")
programımız çalıştığında loglar çalışacak ve kayıtlar.log dosyasına yazılacak.
INFO:root:Bilgi mesajidir
ERROR:root:Hata mesajidir
Tahmin edeceğiniz üzere loglama işlemi için öncelikle logging modülünü içeri aktarmamız gerekir. Log işlemleri için basicConfig fonksiyonunda bazı anahtar kelimeler kullanırız. Bunlardan bazıları: filename, filemode, format, datefmt, level and stream. Biz bu örneğimizde loglama düzeyini bilgi (INFO) olarak belirttik. Ayrıca logların tutulacağı dosya adınıda belirttiğimizede dikkat edin. 5 adet Loglama düzeyi vardır : DEBUG, INFO, WARNING, ERROR ve CRITICAL dir. Varsıyalan olarak, her çalışma anında loglama otomatik şekilde yapılacaktır.Loglamada istisna durumları kayıt altına alabiliriz. Örnek:import logginglogging.basicConfig(filename="hata.log", level=logging.INFO)
log = logging.getLogger("ex")
try:
raise RuntimeError
except RuntimeError:
log.exception("Hata!")
Burada, logging modülünün getLogger yöntemini kullanıyoruz.
Birden çok Modül Kullanarak Loglama ve Logları BiçimlendirmeUygulamalarınızda birden fazla modül kullandığınızı ve kodlarınızın fazla olduğunu ve buradaki işlemlerin bazılarını logladığınızı düşünün. Burada, Fitness Adında bir program geliştirdiğinizi varsayalım. Fitness uygulamasında birden fazla modül ve satırlarca kod içerdiğini ve uygulamanızın loglarının hepsini aynı dosyaya kaydetmek istiyorsanız doğru yerdesiniz. Basit bir yöntemle başlayalım:import logging
import digerModul
def main():
"""
Fitness uygulamasi vs...
:return:
"""
logging.basicConfig(filename="fitness.log", level=logging.INFO)
logging.info("Uygulama Acildi")
sonuc = digerModul.hesapla(2, 1)
logging.info("Basarili!")
if __name__ == "__main__":
main()
Burada, digerModul sınıfının içerdiği kodlar şöyledir:
#digerModul.pyimport loggingdef hesapla(x,y):
"""
diger modul
:param x:
:param y:
:return:
"""
logging.info("%s ve %s toplami: %s" % (x,y, x+y))
return x+y
Eğer projenizin ana kodunu çalıştırırsanız çıktınız yani loglarınız şu şekilde gözükecektir:
INFO:root:Uygulama Acildi
INFO:root:2 ve 1 toplami: 3
INFO:root:Basarili!
Gördüğünüz gibi diğer modüllerdeki logları tek bir log dosyasında topladık. Aslında, bu şekilde yapmamızın büyük projelerde sorun yaratacağını, hangi logun hangi modüle ait olduğunu kestirmek biraz zor olacaktır. Bu yüzden buradaki yanlışı düzeltmemiz gerek. Şimdi farklı bir uygulamaya göz atalım:import logging
import islemModulu
def main():
"""
fitness uygulamasi baslangic noktasi
:return:
"""
logger = logging.getLogger("fitness")
logger.setLevel(logging.INFO)
#file handler yaratiyoruz
fh = logging.FileHandler("fitnessuygulamasi.log")
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
fh.setFormatter(formatter)
#logger objesine handler ekliyoruz
logger.addHandler(fh)
logger.info("Uygulama Baslatildi.")
sonuc = islemModulu.hesapla(3,5)
logger.info("Islem Basarili!")if __name__ == "__main__":
main()
Burada, fitness adında bir loglama örneği oluşturduk. Ayrıca logumuzu formatter metodu ile biçimlendirdik. Örneğimizdeki islemModulu :
#islemModulu.pyimport loggingmodul_logger = logging.getLogger("fitness.islemModulu")def hesapla(x,y):
"""
hesapla fonksiyonu
:param x:
:param y:
:return:
"""
loggerr = logging.getLogger("fitness.islemModulu.hesapla")
loggerr.info("%s ve %s toplami: %s" % (x,y, x+y))
return x+y
Burada 2 adet log tanımlandığına dikkat edin. Buradaki modul_logger ile ilgili hiçbir işlem yapmıyoruz, ancak diğerini kullanıyoruz. Projenizin ana komut dosyasını çalıştırdığınızda loglarınız şu şekilde görünecektir:
2017-01-04 14:50:23,472 - fitness - INFO - Uygulama Baslatildi.
2017-01-04 14:50:23,472 - fitness.islemModulu.hesapla - INFO - 3 ve 5 toplami: 8
2017-01-04 14:50:23,472 - fitness - INFO - Islem Basarili!
root referansının kaldırıldığını görüyorsunuz. Bu hali insanlar tarafından okunuşu kolaylaştırıyor. Bu biçimlendirmeyi yapmak için format yöntemini kullandığımızı unutmayın. Aslında bu yaptığınız biçimlendirme işlemi LogRecord öznitelikleri olarakta bilinir. LogRecord öznitelikleri hakkında daha fazla bilgiye buradan ulaşabilirsiniz.

Logları yapılandırma

Loglar 3 farklı şekilde yapılandırılabilir. Bu makalenin önceki bölümlerinde yaptığımız gibi (loggers, formatters, handlers) yapılandırabilirsiniz. Bir yapılandırma dosyası kullanabilir fileConfig() işlevine geçirebilir yada yapılandırma bilgileri için bir sözlük oluşturabilir ve bunu dictConfig() işlevine verebilirsiniz. Öncelikle bir yapılandırma dosyası oluşturalım ve ardından Python ile nasıl yürütüldüğüne bakalım... İşte bir yapılandırma dosyası örneği:[loggers]
keys=root,fitnessApp
[handlers]
keys=fileHandler, consoleHandler
[formatters]
keys=myFormatter
[logger_root]
level=CRITICAL
handlers=consoleHandler
[logger_fitnessApp]
level=INFO
handlers=fileHandler
qualname=fıtnessApp
[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=myFormatter
args=(sys.stdout,)
[handler_fileHandler]
class=FileHandler
formatter=myFormatter
args=("config.log",)
[formatter_myFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
datefmt=
root ve fitnessApp adında 2 loglama olduğu gözünüzden kaçmamış olabilir. Burada her ne olursa olsun her zaman bir kök(root) bulunmak zorundadır. Python, logging modülünün bir parçası olan config.py'nin _install_loggers işlevinden bir ValueError çağırır. Kök işleyicisini fileHandler olarak ayarlarsanız, günlük çıktısının iki katına çıkmış olursunuz, dolayısıyla bunun oluşmasını engellemek için bunun yerine konsola göndeririz. Bu örneği yakından inceleyin.
import logging
import logging.config
import islemModulu
def main():
"""
Log ayarlari
:return:
"""
logging.config.fileConfig('logging.conf')
logger = logging.getLogger("fitnessApp")
logger.info("Uygulama Basladi..")
sonuc = islemModulu.hesapla(5,8)
logger.info("Basarili..")
if __name__ == "__main__":
main()

Gördüğünüz gibi, tek yapmanız gereken yapılandırma dosyasının yolunu belirtmek. Diğer yapılandırma yöntimi olan dictConfing inceleyelim.
dictConfing Python 2.7 sürümü ve sonraki sürümlerde çalışır. Belirtilen sürümün altında olmadığınızı kontrol ediniz. Aksi halde hatalar alabilirsiniz.import logging
import logging.config
import islemModulu
def main():
"""
#sozluk kullanarak loglama
#https://docs.python.org/3/howto/logging.html#configuring-logging
:return:
"""
dictLogConfig = {
"version":1,
"handlers":{
"fileHandler":{
"class":"logging.FileHandler",
"formatter":"myFormatter",
"fileName":"fitnessApp.log"
}
},
"loggers":{
"fitnessApp":{
"handlers":["fileHandler"],
"level":"INFO"
}
},
"formatters":{
"myFormatter":{
"format":"%(asctime)s - %(name)s - %(levelname)s - %(message)s"
}
}
}
logging.config.dictConfig(dictLogConfig)logger = logging.getLogger("fitnessApp")
logger.info("Uygulama Baslatildi...")
sonuc = islemModulu.hesapla(8,6)
logger.info("Yapilan islem basarili...")
if __name__ == "__main__":
main()

Bu kodu çalıştırdığınızda önceki gibi bir log çıktısı alacaksınız. Bu noktaya kadar bir kaç farklı yapılandırma yapabileceğimizi öğrenmiş-incelemiş olduk. Logging modülü uygulamalarınızda hatalar meydana geldiğinde tespit etmek, sorun gidermek için çok kullanışlı bir modüldür. Büyük bir uygulama yazmadan önce bu modül ile biraz pratik yapmakta fayda olduğunu düşünüyorum.

--

--