search
LoginSignup
32

More than 1 year has passed since last update.

posted at

updated at

Pythonにおけるlogging徹底攻略

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

参考記事

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
32