コンソール画面に出力しているログのうち、INFOレベル以下はstdoutに、WARNINGレベル以上をstderrに出力するサンプルを紹介します。
一般的なコンソールはstderrの出力を強調表示してくれるので見落としが減ります。この設定はしないよりはしておいた方がいいです。
ロギングの細かい解説は長期戦になるのでしないことにして、ここで紹介したサンプルの一部をコピペして流用するのに最低限の最低限、必要かなと感じる部分だけサラッと解説するスタンスでいきます。
よくわからない部分は雰囲気でなんとかしてもらうスタンスで。(笑)
Pythonプログラム内で設定
Pythonプログラム内で全ての設定をするサンプルです。(次の章では同じことを設定ファイルでする方法を紹介します)
実際の設定はinit_logging()関数内でしています。
出力先をstdoutとstderrに分けないといけないので、INFOレベルまでのログだけをstdoutに出力するハンドラと、WARNINGレベル以上のログだけをstderrに出力するハンドラの2つを作っています。
from logging import getLogger, StreamHandler, \
Filter, Formatter, DEBUG, INFO, WARNING
from sys import stdout, stderr
root = getLogger()
class LevelFilter(Filter):
""" max_levelで指定されたログレベルまでしか出力しなくするフィルタを自作 """
def __init__(self, max_level=INFO):
self.max_level = max_level
def filter(self, record):
"""
このメソッドはログ出力メソッドがコールされた時に呼び出されます。
役割は、0か1を戻り値として返すことです。
戻り値が0の場合はログが出力されず、それ以外の場合はログ出力されます。
"""
return record.levelno <= self.max_level
def init_logging():
global root
formatter = Formatter(
'{asctime} {name:<8s} {levelname:<8s} {message}',
style='{')
out_handler = StreamHandler(stdout)
out_handler.setFormatter(formatter)
# WARNING以上がstdoutに出力されないように自作したフィルタをセット
out_handler.addFilter(LevelFilter(INFO))
err_handler = StreamHandler(stderr)
err_handler.setLevel(WARNING)
err_handler.setFormatter(formatter)
root.addHandler(out_handler)
root.addHandler(err_handler)
root.setLevel(DEBUG)
def main():
global root
root.debug('これはデバッグログ')
root.info('これは情報ログ')
root.warning('これは警告ログ')
root.error('これはエラーログ')
root.critical('これはめっちゃヤバい状況のログ')
if __name__ == '__main__':
init_logging()
main()
実行結果:
設定ファイルで設定
ログ出力などの設定は、プログラムに直接書かずに設定ファイルに記述するのが一般的です。
まずはconfig.ymlを作成し、設定をYAML形式で記述します。
version: 1
formatters:
default:
format: '{asctime} {name:<8s} {levelname:<8s} {message}'
style: '{'
filters:
at-most-info:
'()': filter.LevelFilter
max_level: INFO
handlers:
stdout:
class: logging.StreamHandler
stream: ext://sys.stdout
formatter: default
filters:
- at-most-info
stderr:
class: logging.StreamHandler
stream: ext://sys.stderr
formatter: default
level: WARNING
root:
level: DEBUG
handlers:
- stdout
- stderr
config.ymlからfilter.LevelFilterとして参照しやすいように、フィルタークラスはメインプログラムとは分けてfilter.pyに定義しました。
from logging import Filter, INFO, getLevelName
class LevelFilter(Filter):
def __init__(self, max_level=INFO):
if isinstance(max_level, str):
max_level = getLevelName(max_level)
self.max_level = max_level
def filter(self, record):
return record.levelno <= self.max_level
設定ファイルを読み込んでDict形式にするために、ruamel.yamlパッケージをインストールします。
$ pip install ruamel.yaml
そして設定ファイルから読み込んでロギング設定を反映するPythonプログラムconsole_output.pyを書きます。
from logging import getLogger
from ruamel.yaml import YAML
from pathlib import Path
from logging.config import dictConfig
def init_logging():
config_path = Path('config.yml')
config_content = YAML().load(config_path)
dictConfig(config_content)
def main():
root = getLogger()
root.debug('これはデバッグログ')
root.info('これは情報ログ')
root.warning('これは警告ログ')
root.error('これはエラーログ')
root.critical('これはめっちゃヤバい状況のログ')
if __name__ == '__main__':
init_logging()
main()
設定をするためのinit_logging()関数の中身がかなりスッキリしました。
それぞれのファイルは同じフォルダに入っている状態です。
project_dir
|- config.yml
|- filter.py
|- console_output.py
実行結果: