シリアル通信GUIその6
Pythonがログを出力する仕組みってどんな感じ??
ログレコードとは?(logging.LogRecord)
ログ出力されるメッセージそのものの情報をもつもの
ロガー間でやり取りされるものはログレコード
詳しく説明するとログのメッセージ本文や
ログレコードの生成時刻、設定されたログレベル、
呼び出されたソースのファイル名、ソース中の行番号、
スタックトレースの情報を持っていて、自動的に記録される
重要なポイントとしてログレコードを生成するのは
ロガーの役目、出力するのは、ハンドラーの役目
ロガーとは
ログレコードを生成したり、他のロガーに受け渡す
受け取ったログレコードをハンドラに渡す
ハンドラとは
ロガーにひもづけられ、受け取ったログレコードを出力する
フィルタとは
ロガーやハンドラにひもづけられ受け取ったレコードを
処理するか否かを決定する
フォーマッター:ハンドラにひもづけられ、
ハンドラが受け取ったログレコードをフォーマット(整形)する役割を持つ
フォーマッターのコンストラクタにフォーマット文字列を
渡すことでログレコードに含まれる時刻とメッセージの書式設定を
変更する事が出来る
ロガーは実際にロガーにひもづけられたハンドラー(logging.Handler)。
ロガーは各開発者が自分で定義・生成することができます。ロガーはそれぞれ名前(Logger.name)を持ち、
各開発者がロガーを生成するときに、そのモジュール名を
名前に設定することが一般的。
# getLogger関数の第一引数に設定された文字列が、そのロガーの名前になる logger = logging.getLogger(__name__) print(__name__) print(logger.name)
ハンドラーは、ログレコードを出力する
import logging import sys logger = logging.getLogger(__name__) # 標準出力(コンソール)にログを出力するハンドラを生成する sh = logging.StreamHandler(sys.stdout) sh.setLevel(logging.WARNING) # ハンドラをロガーに紐づける logger.addHandler(sh) logger.warning("unauthorized.")
出力例
unauthorized.
デフォルトのフォーマッタはテキスト形式の出力を想定したものですが、
logging.Formatterクラスを継承して独自のフォーマッタを作成し、
JSON形式でログ出力をすることなども可能です。※1
import logging import sys logger = logging.getLogger(__name__) sh = logging.StreamHandler(sys.stdout) sh.setLevel(logging.WARNING) # フォーマッタを定義する(第一引数はメッセージのフォーマット文字列、 #第二引数は日付時刻のフォーマット文字列) fmt = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s", "%Y-%m-%dT%H:%M:%S") # フォーマッタをハンドラに紐づける sh.setFormatter(fmt) logger.addHandler(sh) logger.warning("unauthorized.")
2021-02-17T00:05:13 - __main__ - WARNING - unauthorized
フィルタ(logging.Filter)
ロガーやハンドラが、受け取ったログレコードを
処理するかしないかを決定する条件を定義します。
定義したフィルタは、ロガーやハンドラに紐づけて使用します。
フィルタオブジェクトは、これまで挙げたloggingモジュールのオブジェクトとは
異なり、デフォルトの状態では使われることはありません。
ログレベルだけではコントロールし切れないような、
任意の条件でログのフィルタリングを行いたい場合に
開発者が定義して使用することができます
フィルタサンプルコード
import logging import sys # 指定された文字列がメッセージに含まれている場合のみ処理させるフィルタの例 class TextFilter: def __init__(self, text=""): self.text = text # フィルタメソッド # 処理したい条件ではTrue、処理したくない条件ではFalseを返すような関数を作成。 # 引数recordには、ログレコード(logging.LogRecord)が渡される。 def filter(self, record): if self.text in record.getMessage(): return True return False logger = logging.getLogger(__name__) sh = logging.StreamHandler(sys.stdout) sh.setLevel(logging.WARNING) # フィルタをハンドラに紐づける sh.addFilter(TextFilter("internal server error")) logger.addHandler(sh) logger.warning("unauthorized.") # フィルター条件にマッチしないので出力されない。 logger.warning("internal server error.")
出力例
internal server error.
filter()メソッドを持つオブジェクトを渡すと、
ログレコードをfilter()メソッドのパラメータに入れて呼んでくれます
つまりロガーが階層構造を持っていると
どのような利点があるかと言えば
【ログの出し分けがしやすい】事
「出し分け」は、出力先をファイルとコンソールで
分けたり、出力自体の有無の切り替えをしたりする事
次回は、シリアル通信にあったロガーの命名を行う