Udemy講座 Pythonista ロギングコンフィグ

Python学習【365日チャレンジ!】128日目のマスターU(@Udemy11)です。

3日続けてキングダムの話題になりますが、キングダムにハマってから、秦の始皇帝に対するイメージがかなり変わってきました。

というのも、中国の歴史で一番最初に興味を持ったのが、司馬遼太郎の【項羽と劉邦】だったので、始皇帝は悪政の限りを尽くし、人民を苦しめていたと理解していたからです。

国の支配者もいろいろな側面があるので、一つの側面からしか見れていなかったような気がしています。

キングダムは基本的に史実に基づいてストーリーが進んでいくので(歴史の流れ以外はフィクションだけど)、実際の歴史を学習しながら、こことここは違うなとか、この人物はこんな功績を上げたのかということを学習しながら読めるので、ほんとハマるし、歴史の勉強にもなるんですよね。

ほんと知らなかったことを知るというのは楽しいですね。

それでは、今日もPython学習をはじめましょう!

昨日の復習

昨日は、ロギングのフィルタを使ってログの出力をコントロールする方法について学習しました。

パスワードなどの情報を間違ってログに出力しないようにするためのフィルタがロギングフィルタでした。

プログラムが大きくなってくるとそんな間違いを犯してしまうことがあるのかと疑問に思う反面、そこまでしっかりとフォローしてプログラムを書かないといけないんだなと感じました。

そんなロギングフィルタの使い方についてはこちらの記事をごらんください。

今日は、ロギングの設定をまとめられるロギングコンフィグを学習します。

ロギングコンフィグ

コンフィグは、【configration(コンフィグレーション)】の略で、日本語だと【設定】となります。

つまり、ロギングコンフィグは、ロギングの設定ということです。

これまでの学習で、ロガーを個別に生成して、それぞれのレベルや名前、出力する属性などを指定してログを出力していましたが、必要な設定をすべて一つにまとめて置くことでコードが非常にシンプルになります。

MVCモデルでのアプリ開発同様に、必要なものをまとめることでわかりやすくきれいなコードが書けるということですね。

basicConfigを使ってロギングのレベルを指定したり、出力するファイルを指定したりできましたが、今回は、コンフィグの設定を記述したファイルやコードをまとめる役割を持つfileConfigdictConfigについて学びましょう。

fileConfig

fileConfigは、その名のとおり、ファイルに設定を書き込んだもので、その設定ファイルを読み込んで使うことができます。

設定するファイルはconfigparser形式で書く必要があり、一例として、次のように書くことができます。

[loggers]
keys=root,testLogger

[handlers]
keys=streamHandler

[formatters]
keys=formatter

[logger_root]
level=WARNING
handlers=streamHandler

[logger_testLogger]
level=DEBUG
handlers=streamHandler
propagate=0
qualname=testLogger

[handler_streamHandler]
class=StreamHandler
level=DEBUG
formatter=formatter
args=(sys.stderr,)

[formatter_formatter]
format=%(asctime)s %(name)-10s %(levelname)-8s %(message)s

詳しい環境設定ファイルの書式はこちらのPython公式ドキュメントをごらんください。

[loggers],[handlers],[formatters]の3つは必須のセクションです。

これらのセクションで指定したkeysに対応する詳細セクションの記載も必要で、[logger_root]には、levelhandlersを指定する必要があります。

ルートロガー以外のロガー(今回の設定では[logger_testLogger])セクションには、次の4つの値を指定します。

  • level
  • handlers
  • propagate
  • qualname

propagateは、上位ロガーにも反映する1かこのロガーだけの値にする0に指定し、qualnameは、このロガーを使うための名前(今回はtestLogger)になります。

次のhandlerのセクション([handler_streamHandler])には、一例として次の4つの値を指定しています。

  • class
  • level
  • formatter
  • args

classは、ハンドラのクラス(今回はStreamHandler)を指定し、levelは、ルートロガーの場合はルートが優先されます。

formatterで指定した名前(formatter)は、26行目の[formatter_formatter]で使われ、27行目でフォーマットを指定しています。

argsには、writeメソッドを持った標準エラーを出力するファイルオブジェクトsys.stderrを指定しています。

最後の27行目のformatの指定で-12-8が入っているのは、取得するnamelevelnameの最長のlengthを指定し、属性出力の見た目を揃えるためです。

例えばnameは、roottestLoggerになるので、最長のlength10なので、%(name)-10sにしていて、%(levelname)-8sCRITICALが最長の8ということです。

fileConfigを使ったログ出力

上記のconfig.iniを使ってログ出力をしてみます。

まずは次のコードです。

import logging.config


logging.config.fileConfig('config.ini')
logger = logging.getLogger(__name__)

logger.debug('debug test')
logger.info('info test')
logger.warning('warning test')
logger.error('error test')
logger.critical('critical test')

出力結果

2020-06-17 22:39:23,562 __main__   WARNING  warning test
2020-06-17 22:39:23,562 __main__   ERROR    error test
2020-06-17 22:39:23,562 __main__   CRITICAL critical test

ロガーが__main__、つまりrootになるので、config.ini[logger_root]で指定されているレベルWARNINGまでしか出力されません。

続いて5行目だけ次のように__name__testLoggerに変えてみます。

logger = logging.getLogger('testLogger')

出力結果

2020-06-17 22:42:38,329 testLogger DEBUG    debug test
2020-06-17 22:42:38,329 testLogger INFO     info test
2020-06-17 22:42:38,329 testLogger WARNING  warning test
2020-06-17 22:42:38,329 testLogger ERROR    error test
2020-06-17 22:42:38,329 testLogger CRITICAL critical test

生成されるロガーの名前がtestLoggerになるので、config.ini[logger_testLogger]で指定されているレベルであるDEBUGまで出力されています。

ロギングの設定を外部ファイルに切り分けることで、メインのコードをスッキリできることが理解できたかと思います。

次に、この設定をまとめて実行ファイルに記述するdictConfigを学習しましょう。

dictConfig

fileConfig同様に設定をまとめることができ、外部設定ファイルではなく、dict型でPythonファイルに直接記述することができます。

実際のコードをみてみましょう。

import logging.config


logging.config.dictConfig({
    'version': 1,
    'formatters': {
        'testFormatter':{
            'format': '%(asctime)s %(name)-10s %(levelname)-8s %(message)s'}
    },
    'handlers': {
        'testHandlers': {
            'class': 'logging.StreamHandler',
            'formatter': 'testFormatter',
            'level': logging.DEBUG
        }
    },
    'root': {
        'handlers': ['testHandlers'],
        'level': logging.WARNING
    },
    'loggers': {
        'testLogger': {
            'handlers': ['testHandlers'],
            'level': logging.DEBUG,
            'propagate': 0
        }
    }
})

logger = logging.getLogger(__name__)

logger.debug('debug test')
logger.info('info test')
logger.warning('warning test')
logger.error('error test')
logger.critical('critical test')

fileConfigと比べると指定する値が減っているような感じです。

dictConfigでロギング設定をするには、環境設定辞書スキーマに従った記述でキーを設定する必要があります。

今回設定しているキーは、

  • version
  • formatters
  • handlers
  • root
  • loggers

の5つです。

必須のキーはversionだけで、現状で指定できるのは1だけです。

formattersで、生成するインスタンス名をtestFormatterに指定し、formatメソッドに、属性を指定しています。

handlersでは、生成するインスタンス名をtestHandlersとし、クラスをStreamHandlerformatterに先に設定しているtestFormatterlevelDEBUGを指定しています。

rootには、handlerlevelを指定しています。
testHandlerが指定されていて、そちらのlevelDEBUGですが、rootで指定しているレベルのWARNINGが優先されます。

最後のloggersは、testLoggerがロガー名になり、次の4つのキーを指定することができます。

  • level
  • handlers
  • filters
  • propagate

今回は、filterを指定せずに、3つのキーを指定していますが、これらのキーは必須ではなく、任意で指定することができます。

dictConfigのログ出力

fileConfigのログ出力同様に、30行目の__name__でログ出力をすると17行目から20行目で設定したWARNINGレベルまでが出力されます。

30行目の__name__を21行目から27行目で設定しているtestLoggerに変更すると、レベルはDEBUGになるので、32行目から36行目のログが出力されます。

dictConfigのほうが使われる?

fileConfigdictConfigを学習してきましたが、よく使われているのはdictConfigの方だそうです。

これは、Webアプリケーションなどの開発で、dictConfigを使って、setting.pyなどの設定ファイルを作ってインポートする使い方が主流となっているためのようです。

最初はちんぷんかんぷん

コンフィグに関するコードを見るとちょっとモチベーションがさがっちゃうんですよね。

一つ一つのコードを読み解きながら理解しないといけないので、めっちゃ時間がかかるんです。

ある程度コードを読み解いてきたとはいえ、わからないコードがたくさん出てくるので、最初はまったくもってちんぷんかんぷんで、レクチャーを受講したあとは、Pythonの公式ドキュメントやWeb上の記事とにらめっこしながら理解しなければなりません。

ほんと根気が必要なんですよね。

わからないことをすぐに答えてくれる人が近くにいると、すぐに問題は解決するんでしょうけど、それだと十分に理解できていないまま次に進むことになります。

自分で右往左往しながらきっちりと理解して、ちょっとずつ進んでいかないとなかなか自分のものにはできません。

今回のConfigについても、カバーできていないセクションやキーがありますが、公式ドキュメントを読めば理解できるということがわかったので、実践しながらスキルを磨いていこうと思っています。

ということで、今日はこのへんで終了します。

それでは明日も、Good Python!