Logging Python tidak menghasilkan apa pun

97

Dalam skrip python yang saya tulis, saya mencoba mencatat peristiwa menggunakan modul logging. Saya memiliki kode berikut untuk mengkonfigurasi logger saya:

ERROR_FORMAT = "%(levelname)s at %(asctime)s in %(funcName)s in %(filename) at line %(lineno)d: %(message)s"
DEBUG_FORMAT = "%(lineno)d in %(filename)s at %(asctime)s: %(message)s"
LOG_CONFIG = {'version':1,
              'formatters':{'error':{'format':ERROR_FORMAT},
                            'debug':{'format':DEBUG_FORMAT}},
              'handlers':{'console':{'class':'logging.StreamHandler',
                                     'formatter':'debug',
                                     'level':logging.DEBUG},
                          'file':{'class':'logging.FileHandler',
                                  'filename':'/usr/local/logs/DatabaseUpdate.log',
                                  'formatter':'error',
                                  'level':logging.ERROR}},
              'root':{'handlers':('console', 'file')}}
logging.config.dictConfig(LOG_CONFIG)

Ketika saya mencoba untuk menjalankan logging.debug("Some string"), saya tidak mendapatkan output ke konsol, meskipun halaman ini di dokumen mengatakan bahwa logging.debugseharusnya root logger mengeluarkan pesan. Mengapa program saya tidak mengeluarkan apa pun, dan bagaimana cara memperbaikinya?

murgatroid99
sumber

Jawaban:

103

Tingkat pencatatan default adalah peringatan. Karena Anda belum mengubah level, level root logger masih memberi peringatan. Artinya, ia akan mengabaikan logging apa pun dengan level yang lebih rendah dari peringatan, termasuk logging debug.

Ini dijelaskan dalam tutorial :

import logging
logging.warning('Watch out!') # will print a message to the console
logging.info('I told you so') # will not print anything

Baris 'info' tidak mencetak apa pun, karena levelnya lebih tinggi dari info.

Untuk mengubah level, cukup setel di root logger:

'root':{'handlers':('console', 'file'), 'level':'DEBUG'}

Dengan kata lain, tidak cukup hanya mendefinisikan penangan dengan level = DEBUG, tingkat logging sebenarnya juga harus DEBUG untuk membuatnya menghasilkan apa pun.

Omri Barel
sumber
7
Dokumentasi mengatakan bahwa level default-nya adalah NOTSET yang merupakan level 0 yang harus menampilkan semuanya ... Mengapa ini tidak benar?
Ben
@ Ben mana yang mengatakan itu? Yang bisa saya lihat hanyalah "Tingkat default adalah PERINGATAN, yang berarti bahwa hanya peristiwa tingkat ini dan di atasnya yang akan dilacak, kecuali paket logging dikonfigurasi untuk melakukan sebaliknya."
Omri Barel
1
@Ben menurut dokumen, para penebang dilintasi untuk menemukan induk pertama dengan level != NOTSETatau root (jika tidak ada yang ditemukan). Akar memiliki WARNINGlevel secara default. Ini ditulis di bagian yang Anda tautkan ke ( Logger.setLevel).
Omri Barel
5
Ingatlah bahwa setelah mengimpor loggingAnda perlu menelepon logging.basicConfig()setidaknya sekali. Jika tidak, Anda mungkin akan terkejut bahwa penebang kayu tidak akan mencetak apa pun. Fungsi pencatatan di root logger memanggilnya dengan malas.
Hubert Grzeskowiak
74

Bertahun-tahun kemudian, tampaknya masih ada masalah kegunaan dengan logger Python. Berikut beberapa penjelasan dengan contoh:

import logging
# This sets the root logger to write to stdout (your console).
# Your script/app needs to call this somewhere at least once.
logging.basicConfig()

# By default the root logger is set to WARNING and all loggers you define
# inherit that value. Here we set the root logger to NOTSET. This logging
# level is automatically inherited by all existing and new sub-loggers
# that do not set a less verbose level.
logging.root.setLevel(logging.NOTSET)

# The following line sets the root logger level as well.
# It's equivalent to both previous statements combined:
logging.basicConfig(level=logging.NOTSET)


# You can either share the `logger` object between all your files or the
# name handle (here `my-app`) and call `logging.getLogger` with it.
# The result is the same.
handle = "my-app"
logger1 = logging.getLogger(handle)
logger2 = logging.getLogger(handle)
# logger1 and logger2 point to the same object:
# (logger1 is logger2) == True


# Convenient methods in order of verbosity from highest to lowest
logger.debug("this will get printed")
logger.info("this will get printed")
logger.warning("this will get printed")
logger.error("this will get printed")
logger.critical("this will get printed")


# In large applications where you would like more control over the logging,
# create sub-loggers from your main application logger.
component_logger = logger.getChild("component-a")
component_logger.info("this will get printed with the prefix `my-app.component-a`")

# If you wish to control the logging levels, you can set the level anywhere 
# in the hierarchy:
#
# - root
#   - my-app
#     - component-a
#

# Example for development:
logger.setLevel(logging.DEBUG)

# If that prints too much, enable debug printing only for your component:
component_logger.setLevel(logging.DEBUG)


# For production you rather want:
logger.setLevel(logging.WARNING)

Sumber kebingungan yang umum berasal dari root logger yang diinisialisasi dengan buruk. Pertimbangkan ini:

import logging
log = logging.getLogger("myapp")
log.warning("woot")
logging.basicConfig()
log.warning("woot")

Keluaran:

woot
WARNING:myapp:woot

Bergantung pada lingkungan waktu proses dan tingkat logging Anda, baris log pertama (sebelum konfigurasi dasar) mungkin tidak muncul di mana pun .

Hubert Grzeskowiak
sumber
Logging saya tidak berfungsi, karena tidak menghasilkan file keluaran. Apakah Anda melihat sesuatu yang saya lakukan itu salah? logging.basicConfig( filename='logging.txt', level=logging.DEBUG) logger = logging.getLogger() logger.info('Test B') logging.info('Test A')
Rylan Schaeffer
File logging bahkan tidak dibuat
Rylan Schaeffer
Saya perhatikan ketika saya menjatuhkan break point setelahnya logger = logging.getLogger(), levelnya disetel ke PERINGATAN meskipun saya menentukan levelnya sebagai DEBUG. Apakah Anda tahu apa yang saya lakukan salah?
Rylan Schaeffer
Hai @RylanSchaeffer, Anda mungkin ingin membuat pertanyaan baru dan memberikan detail lebih lanjut. Ini juga akan memberi orang lain kesempatan untuk membantu Anda.
Hubert Grzeskowiak
Aku melakukannya. Seringkali, mengajukan komentar adalah cara yang lebih cepat untuk menemukan jawaban karena setidaknya satu orang yang berpengetahuan akan melihat pertanyaan saya
Rylan Schaeffer
26

Untuk siapa pun di sini yang menginginkan jawaban yang sangat sederhana: cukup setel level yang ingin Anda tampilkan. Di bagian atas semua skrip saya, saya hanya meletakkan:

import logging
logging.basicConfig(level = logging.INFO)

Kemudian untuk menampilkan apapun pada atau di atas level itu:

logging.info("Hi you just set your fleeb to level plumbus")

Ini adalah rangkaian lima tingkat hierarki sehingga log akan ditampilkan pada tingkat yang Anda tetapkan, atau lebih tinggi . Jadi jika ingin menampilkan error bisa anda gunakan logging.error("The plumbus is broken").

Tingkat, dalam rangka meningkatkan keparahan, yang DEBUG, INFO, WARNING, ERROR, dan CRITICAL. Pengaturan defaultnya adalah WARNING.

Ini adalah artikel bagus yang berisi informasi ini yang diungkapkan lebih baik daripada jawaban saya:
https://www.digitalocean.com/community/tutorials/how-to-use-logging-in-python-3

eric
sumber
14

Mungkin coba ini? Tampaknya masalah terpecahkan setelah menghapus semua penangan dalam kasus saya.

for handler in logging.root.handlers[:]:
    logging.root.removeHandler(handler)

logging.basicConfig(filename='output.log', level=logging.INFO)
yue dong
sumber
SyntaxError: invalid syntax
Eric
2
Mengapa ini perlu? Penangan apa yang tersedia dengan python logger dan mengapa mereka ada di sana untuk memulai? Atau mungkin pertanyaannya adalah, mengapa basicConfig tidak menimpa / menggantinya?
jrh