Python Günlüğü: Derinlemesine Bir Eğitim
Yayınlanan: 2022-03-11Uygulamalar daha karmaşık hale geldikçe, iyi günlüklere sahip olmak, yalnızca hata ayıklama sırasında değil, aynı zamanda uygulama sorunları/performansı için fikir sağlamak için de çok yararlı olabilir.
Python standart kitaplığı, temel günlük kaydı özelliklerinin çoğunu sağlayan bir günlük kaydı modülüyle birlikte gelir. Doğru bir şekilde ayarlandığında, bir günlük mesajı, günlüğün ne zaman ve nerede başlatıldığı ve ayrıca çalışan işlem/iş parçacığı gibi günlük bağlamı hakkında birçok yararlı bilgi getirebilir.
Avantajlarına rağmen, düzgün bir şekilde kurulması biraz zaman aldığından ve bence tam olmasına rağmen, https://docs.python.org/3/library/logging.html adresindeki resmi günlük kaydı belgesi olduğundan, günlük kaydı modülü genellikle gözden kaçar. gerçekten günlüğe kaydetme konusunda en iyi uygulamaları vermez veya bazı günlüğe kaydetme sürprizlerini vurgulamaz.
Bu Python günlüğü öğreticisi, günlük kaydı modülüyle ilgili eksiksiz bir belge olmayı değil, bazı günlük kaydı kavramlarını ve ayrıca dikkat edilmesi gereken bazı "önemsiz şeyleri" tanıtan bir "başlangıç" kılavuzu anlamına gelir. Gönderi, en iyi uygulamalarla sona erecek ve daha gelişmiş günlük kaydı konularına yönelik bazı işaretçiler içerecektir.
Lütfen gönderideki tüm kod parçacıklarının, kayıt modülünü zaten içe aktardığınızı varsaydığını unutmayın:
import logging
Python Günlüğü Kavramları
Bu bölüm, günlük kaydı modülünde sıklıkla karşılaşılan bazı kavramlara genel bir bakış sunar.
Python Günlüğü Seviyeleri
Günlük düzeyi, bir günlüğe verilen "önem"e karşılık gelir: bir "hata" günlüğü, "uyar" günlüğünden daha acil olmalıdır, oysa bir "hata ayıklama" günlüğü yalnızca uygulamada hata ayıklarken yararlı olmalıdır.
Python'da altı günlük düzeyi vardır; her düzey, günlük önem derecesini gösteren bir tamsayı ile ilişkilendirilir: NOTSET=0, DEBUG=10, INFO=20, WARN=30, ERROR=40 ve CRITICAL=50.
Özelliği daha sonra ele alınacak olan NOTSET dışında tüm seviyeler oldukça basittir (DEBUG < INFO < WARN ).
Python Günlük Biçimlendirme
Günlük biçimlendirici, temel olarak bir günlük mesajını, ona bağlam bilgisi ekleyerek zenginleştirir. Günlüğün ne zaman, nereye (Python dosyası, satır numarası, yöntem, vb.) gönderildiğini ve iş parçacığı ve işlem gibi ek bağlamı (çok iş parçacıklı bir uygulamada hata ayıklarken son derece yararlı olabilir) bilmek yararlı olabilir.
Örneğin, bir günlük biçimlendirici aracılığıyla bir "merhaba dünya" günlüğü gönderildiğinde:
"%(asctime)s — %(name)s — %(levelname)s — %(funcName)s:%(lineno)d — %(message)s"
olacak
2018-02-07 19:47:41,864 - abc - WARNING - <module>:1 - hello world
Python Günlük İşleyicisi
Günlük işleyici, bir günlüğü etkili bir şekilde yazan/görüntüleyen bileşendir: Bunu konsolda (StreamHandler aracılığıyla), bir dosyada (FileHandler aracılığıyla) veya hatta size SMTPHandler vb. aracılığıyla bir e-posta göndererek görüntüleyin.
Her günlük işleyicinin 2 önemli alanı vardır:
- Bir günlüğe bağlam bilgisi ekleyen bir biçimlendirici.
- Düzeyleri daha düşük olan günlükleri filtreleyen bir günlük düzeyi. Bu nedenle, INFO düzeyine sahip bir günlük işleyici, DEBUG günlüklerini işlemeyecektir.
Standart kitaplık, yaygın kullanım durumları için yeterli olması gereken bir avuç işleyici sağlar: https://docs.python.org/3/library/logging.handlers.html#module-logging.handlers. En yaygın olanları StreamHandler ve FileHandler'dır:
console_handler = logging.StreamHandler() file_handler = logging.FileHandler("filename")
Python Kaydedici
Logger, muhtemelen kodda doğrudan en sık kullanılacak olan ve aynı zamanda en karmaşık olanıdır. Yeni bir kaydedici şu şekilde elde edilebilir:
toto_logger = logging.getLogger("toto")
Bir kaydedicinin üç ana alanı vardır:
- Propagate: Bir günlüğün, günlükçünün ebeveynine yayılıp yayılmayacağına karar verir. Varsayılan olarak değeri True'dur.
- A düzeyi: Günlük işleyici düzeyi gibi, günlükçü düzeyi de "daha az önemli" günlükleri filtrelemek için kullanılır. Bunun dışında, günlük işleyiciden farklı olarak, seviye yalnızca "alt" günlükçüde kontrol edilir; günlük üstlerine yayıldığında, seviye kontrol edilmeyecektir. Bu oldukça sezgisel olmayan bir davranıştır.
- İşleyiciler: Bir günlüğün bir günlükçüye ulaştığında gönderileceği işleyicilerin listesi. Bu, esnek bir günlük işlemeye olanak tanır; örneğin, tüm DEBUG günlüklerini günlüğe kaydeden bir dosya günlüğü işleyicisine ve yalnızca KRİTİK günlükler için kullanılacak bir e-posta günlüğü işleyicisine sahip olabilirsiniz. Bu bağlamda, kaydedici-işleyici ilişkisi, yayıncı-tüketici ilişkisine benzer: Günlük, günlük, günlük, günlük düzeyi denetimini geçtikten sonra tüm işleyicilere yayınlanacaktır.
Bir günlükçü ada göre benzersizdir , yani "toto" adlı bir günlükçü oluşturulmuşsa, logging.getLogger("toto")
sonuç çağrıları aynı nesneyi döndürür:
assert id(logging.getLogger("toto")) == id(logging.getLogger("toto"))
Tahmin edebileceğiniz gibi, kaydedicilerin bir hiyerarşisi vardır. Hiyerarşinin en üstünde, logging.root aracılığıyla erişilebilen kök kaydedici bulunur. Bu günlükçü, logging.debug()
gibi yöntemler kullanıldığında çağrılır. Varsayılan olarak, kök günlük düzeyi WARN'dır, bu nedenle daha düşük düzeyli her günlük (örneğin logging.info("info")
aracılığıyla) yoksayılır. Kök günlükçüsünün bir başka özelliği de, varsayılan işleyicisinin, WARN'dan daha yüksek bir düzeye sahip bir günlük ilk kez günlüğe kaydedildiği zaman oluşturulacak olmasıdır. logging.debug()
gibi yöntemlerle doğrudan veya dolaylı olarak kök kaydedicinin kullanılması genellikle önerilmez.

Varsayılan olarak, yeni bir günlükçü oluşturulduğunda, üst öğesi kök günlükçüye ayarlanacaktır:
lab = logging.getLogger("ab") assert lab.parent == logging.root # lab's parent is indeed the root logger
Ancak, kaydedici "nokta gösterimini" kullanır, yani "ab" adlı bir günlükçü, "a" kaydedicisinin alt öğesi olacaktır. Ancak, bu yalnızca “a” günlükçü oluşturulmuşsa geçerlidir, aksi takdirde “ab” ebeveyni hala köktür.
la = logging.getLogger("a") assert lab.parent == la # lab's parent is now la instead of root
Bir günlükçü, seviye kontrolüne göre bir günlüğün geçip geçmeyeceğine karar verdiğinde (örneğin, günlük seviyesi, günlükçü seviyesinden düşükse, günlük yok sayılır), gerçek seviye yerine “etkin seviyesini” kullanır. Seviye NOTSET değilse, yani DEBUG'dan CRITICAL'e kadar tüm değerler, etkin seviye kaydedici seviyesi ile aynıdır; ancak, günlükçü seviyesi NOTSET ise, etkin seviye, NOTSET olmayan bir seviyeye sahip olan ilk üst seviye olacaktır.
Varsayılan olarak, yeni bir günlükçü NOTSET düzeyine sahiptir ve kök günlükçü bir WARN düzeyine sahip olduğundan, günlükçünün etkin düzeyi WARN olacaktır. Bu nedenle, yeni bir günlükçüye bazı işleyiciler eklenmiş olsa bile, günlük düzeyi WARN'ı geçmediği sürece bu işleyiciler çağrılmaz:
toto_logger = logging.getLogger("toto") assert toto_logger.level == logging.NOTSET # new logger has NOTSET level assert toto_logger.getEffectiveLevel() == logging.WARN # and its effective level is the root logger level, ie WARN # attach a console handler to toto_logger console_handler = logging.StreamHandler() toto_logger.addHandler(console_handler) toto_logger.debug("debug") # nothing is displayed as the log level DEBUG is smaller than toto effective level toto_logger.setLevel(logging.DEBUG) toto_logger.debug("debug message") # now you should see "debug message" on screen
Varsayılan olarak, günlük geçişlerine karar vermek için günlükçü seviyesi kullanılacaktır: Günlük seviyesi, günlükçü seviyesinden düşükse, günlük yok sayılacaktır.
Python Günlüğü En İyi Uygulamaları
Günlüğe kaydetme modülü gerçekten çok kullanışlıdır, ancak en iyi Python geliştiricileri için bile uzun saatler boyunca baş ağrısına neden olabilecek bazı tuhaflıklar içerir. Bence bu modülü kullanmak için en iyi uygulamalar:
- Kök kaydediciyi yapılandırın, ancak onu asla kodunuzda kullanmayın; örneğin, kök kaydediciyi sahne arkasında çağıran
logging.info()
gibi bir işlevi asla çağırmayın. Kullandığınız kitaplıklardan hata mesajlarını yakalamak istiyorsanız, örneğin hata ayıklamayı kolaylaştırmak için kök günlükçüsünü bir dosyaya yazacak şekilde yapılandırdığınızdan emin olun. Varsayılan olarak, kök günlükçü yalnızca çıktı verirstderr
, böylece günlük kolayca kaybolabilir. - Günlüğü kullanmak için,
logging.getLogger(logger name)
kullanarak yeni bir günlükçü oluşturduğunuzdan emin olun. Genelde kaydedici adı olarak__name__
kullanırım, ancak tutarlı olduğu sürece her şey kullanılabilir. Daha fazla işleyici eklemek için genellikle bir günlükçü döndüren bir yöntemim var (özü https://Gist.github.com/nguyenkims/e92df0f8bd49973f0c94bddf36ed7fd0 adresinde bulabilirsiniz).
import logging import sys from logging.handlers import TimedRotatingFileHandler FORMATTER = logging.Formatter("%(asctime)s — %(name)s — %(levelname)s — %(message)s") LOG_FILE = "my_app.log" def get_console_handler(): console_handler = logging.StreamHandler(sys.stdout) console_handler.setFormatter(FORMATTER) return console_handler def get_file_handler(): file_handler = TimedRotatingFileHandler(LOG_FILE, when='midnight') file_handler.setFormatter(FORMATTER) return file_handler def get_logger(logger_name): logger = logging.getLogger(logger_name) logger.setLevel(logging.DEBUG) # better to have too much log than not enough logger.addHandler(get_console_handler()) logger.addHandler(get_file_handler()) # with this pattern, it's rarely necessary to propagate the error up to parent logger.propagate = False return logger
Yeni bir günlükçü oluşturup kullandıktan sonra:
my_logger = get_logger("my module name") my_logger.debug("a debug message")
- Dosya bir boyut sınırına ulaştığında veya her gün yaptığında dosyayı sizin için otomatik olarak döndüreceğinden, FileHandler yerine örnekte kullanılan TimedRotatingFileHandler gibi RotatingFileHandler sınıflarını kullanın.
- Hata günlüklerini sizin için otomatik olarak yakalamak için Sentry, Airbrake, Raygun vb. araçları kullanın. Bu, özellikle günlüğün çok ayrıntılı olabileceği ve hata günlüklerinin kolayca kaybolabileceği bir web uygulaması bağlamında kullanışlıdır. Bu araçları kullanmanın bir başka avantajı da, hatadaki değişken değerler hakkında ayrıntılı bilgi alabilmenizdir, böylece hangi URL'nin hatayı tetiklediğini, hangi kullanıcının ilgilendiğini vb. bilebilirsiniz.
Daha fazla en iyi uygulamayla ilgileniyorsanız, Toptaler Martin Chikilian'ın Python Geliştiricilerinin Yaptığı En Yaygın 10 Hatayı okuyun.