この記事はQiitaに投稿した記事の転載です
loggingの重要ポイント
ルートのloggingを汚さないよう、getLoggerでloggerオブジェクトを作り、ロギングを行うことが望ましい。また、コード中で誤ってloggingを汚さないように下記の良い例のようにimportを工夫すると良い。
# 良くない例
import logging
logging.debug('this is bad logging') # 良い例
from logging import getLogger # 途中で誤ってloggingを汚さないように個別にimport
logger = getLogger(__name__)
logger.debug('this is good logging') logger作成時に__name__としておくことで、出力に__name__の値を表示することが出来る。
ログレベルについて
loggingにはログレベルが設定でき、setLevelによりどのログレベルまで表示を行うかを制御することが可能。
ログレベルには、DEBUG, INFO, WARNING, ERROR, CRITICALの5つがあり、ユーザーが任意に指定する必要がある。これらはloggerや後述の各ハンドラーにも個別に設定することが出来る。
from logging import getLogger, INFO
logger = getLogger(__name__)
logger.setLevel(INFO) # この場合、INFO以上のログが出力。つまり、DEBUG以外は出力される
logger.debug('debug log') # 今回の場合、これは表示されない
logger.info('info log')
logger.warning('warning log')
logger.error('error log')
logger.critical('critical log') 出力結果
INFO:root:info log
WARNING:root:warning log
ERROR:root:error log
CRITICAL:root:critical log ※loggerに設定されたログレベルは後述のハンドラーにも継承される。
下記はログレベルの詳細の表である。ログレベルに与えられた数値は任意に設定することで、ユーザー定義のログレベルを設定することもできる。利用するタイミングについては任意であるため、必ずしも下記の表に準拠する必要はない。(参考程度)
| ログレベル | 数値 | 利用するタイミング(任意) |
|---|---|---|
DEBUG | 10 | 問題探求に必要な詳細な情報を出力したい場合 |
INFO | 20 | 想定内の処理が行われた場合 |
WARNING | 30 | 想定外の処理やそれが起こりそうな場合 |
ERROR | 40 | 重大な問題により、機能を実行出来ない場合 |
CRITICAL | 50 | プログラムが実行不可となるような重大なエラーが発生した場合 |
※Logging HOWTOより引用
フォーマットについて
loggingにはフォーマットも追加でき、setFormatterによりログに日付や関数、ログレベルの情報など様々な詳細情報を出力できるようになる。これもログレベルと同じく、loggerや各ハンドラーごとに個別に設定することが出来る。
from logging import getLogger, Formatter
logger = getLogger(__name__)
format = Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger.setFormatter(format) # loggerにフォーマットを適用
logger.info('this is format') 出力結果
2021-01-13 16:40:23 - __main__ - INFO - this is format ※その他の利用できるフォーマットについては、logging --- Python 用ロギング機能のLogRecord属性を参照
ハンドラーの設定(StreamHandler, FileHandler)
logを出力するためには、ハンドラーを作成する必要がある。
StreamHandlerはコンソール出力用、FileHandlerはログファイル出力用に設定する。
from logging import getLogger, StreamHandler, FileHandler, Formatter, INFO, ERROR
logger = getLogger(__name__)
logger.setLevel(INFO) # loggerがINFOに設定されていると、ハンドラーにもINFO以上のログのみ継承される
# StreamHandlerの設定
ch = StreamHandler()
ch.setLevel(INFO) # ハンドラーにもそれぞれログレベル、フォーマットの設定が可能
ch_formatter = Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger.addHandler(ch) # StreamHandlerの追加
# FileHandlerの設定
fh = FileHandler('log/test.log') # 引数には出力ファイルのパスを指定
fh.setLevel(ERROR) # ハンドラーには、logger以下のログレベルを設定することは出来ない(この場合、DEBUGは不可)
fh_formatter = Formatter('%(asctime)s - %(levelname)s - %(filename)s - %(name)s - %(funcName)s - %(message)s')
logger.addHandler(fh) # FileHandlerの追加 ※その他の有用なハンドラーは、logging.handlers --- ロギングハンドラを参照
コンフィグを利用したログの設定
logging.configを利用して、上記のようなハンドラーやフォーマットの個別の設定を読み込ませることもできる。
fileConfig(コンフィグファイルで設定する方法)
from logging import getLogger
import logging.config
logging.config.fileConfig('logging_settings.ini') # ファイルから設定を読み込む
logger = getLogger('root') # logging_settings.iniのrootの設定が呼び出される
logger.info('this is fileConfig from logging_settings.ini') logging_settings.ini
[loggers] ; getLoggerで参照する名前を登録(rootは__main__の時に自動で取得)
keys=root
[handlers] ; 下記のhandler_xxxで設定したものを記述(複数の場合はコンマ区切り)
keys=newStreamhandler
[formatters] ; 下記のformatter_xxxで設定したものを記述(複数の場合はコンマ区切り)
keys=newFormatter
[logger_root] ; 各loggerの設定(logger_<loggerのkey名>とする)
level=INFO
handlers=newStreamhandler
[handler_newStreamhandler]
class=Streamhandler
level=DEBUG
formatter=newFormatter
args=(sys.stderr,)
[formatter_newFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s 出力結果
2021-01-13 16:40:23 - root - INFO - this is fileConfig from logging_settings.ini dictConfig(辞書型で設定する方法)
from logging import getLogger, DEBUG, INFO
import logging.config
dict_logging_settings = {
'version': 1, # おまじない
# 新しいハンドラーを追加する場合は、handlers内にnewStreamhandlerと同じ形式で追加
'handlers': {
'newStreamHandler': {
'class': 'logging.StreamHandler',
'formatter': 'newFormatter',
'level': DEBUG
}
},
# 新しいフォーマットを追加する場合は、formatters内にnewFormatterと同じ形式で追加
'formatters': {
'newFormatter': {
'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
},
},
# rootのloggerのみ例外的にloggersの外で設定
'root': {
'handlers': ['newStreamHandler'],
'level': INFO
},
# 他に追加したいloggerについては、上記のrootと同じようにloggers内に記述する
'loggers': {}
}
logging.config.dictConfig(dict_logging_settings) # 辞書から設定を読み込む
logger = getLogger('root') # dict_logging_settingsのrootの設定が呼び出される
logger.info('this is dictConfig from dict_logging_settings') 出力結果
2021-01-13 16:40:23 - root - INFO - this is dictConfig from dict_logging_settings