当サイトは、アフィリエイト広告を利用しています
Pythonでログ出力を行いたい場合はloggingを使うことになるので
loggingの基本的な使い方や用語をまとめて忘備録として残しておく
Pythonのversionは3.12で試す。
pythonでログを出す方法としては大きく
の2つの方法がある。
pythonでプログラムの実行結果当を出す方法としては
下記のような感じでprintを使う。
def main():val = 1 + 1print(f'1+1の計算結果は{val}です')if __name__ == "__main__":main()# 実行結果# 1+1の計算結果は2です
ただこれだと結果がターミナルで出力されるだけなので
開発時に動作を確認するには使えるが物足りない。
※ファイルに出すこともできるがそれならloggingを使ったほうがいいので割愛。
Python標準のloggingモジュールを使うと下記のようになる
import logging# ロガーを作成logger = logging.getLogger(__name__)logger.setLevel(logging.DEBUG)# コンソールハンドラーを作成してロガーに追加console_handler = logging.StreamHandler()console_handler.setLevel(logging.DEBUG)# フォーマッターを作成してコンソールハンドラーに追加formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')console_handler.setFormatter(formatter)# コンソールハンドラーをロガーに追加logger.addHandler(console_handler)def main():val = 1 + 1logger.info(f'1+1の計算結果は{val}です')if __name__ == "__main__":main()# 実行結果# 2024-07-30 17:15:38,752 - INFO - 1+1の計算結果は2です
少し設定は増えるが利点は多い。
loggingモジュールを使うと下記のようなことができるようになる
loggingモジュールではログメッセージに対して異なる
重要度レベル
を設定できるので重要度に応じたログフィルタリングや処理を行える。
loggingモジュールではログメッセージのフォーマットを柔軟に設定できる
日時、ログレベル、メッセージ内容などを含むカスタムフォーマットを指定可能
loggingモジュールではログの出力先を簡単に設定できる
※ファイル、コンソール、ネットワーク、メールなどなど
システムやアプリケーションにログ機能を実装する場合は
上記のような利点があるためloggingモジュールを使う。
printは基本的にはデバッグや簡単なスクリプトを実行する際に
使うようにする。
loggingモジュールを使うとログレベルを設定する必要がある。
ログレベルは
があり、重要度の順序が
DEBUG < INFO < WARNING < ERROR < CRITICAL
となっている。
例えばログレベルを「WARNING」にした場合
import logging# ロガーを作成logger = logging.getLogger(__name__)logger.setLevel(logging.DEBUG)# コンソールハンドラーを作成してロガーに追加console_handler = logging.StreamHandler()# ログレベルを設定console_handler.setLevel(logging.WARNING)# フォーマッターを作成してコンソールハンドラーに追加formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')console_handler.setFormatter(formatter)# コンソールハンドラーをロガーに追加logger.addHandler(console_handler)def main():logger.debug('ログレベル:debug')logger.info('ログレベル:info')logger.warning('ログレベル:warning')logger.error('ログレベル:error')logger.critical('ログレベル:critical')if __name__ == "__main__":main()# 実行結果# 2024-07-30 17:23:56,375 - WARNING - ログレベル:warning# 2024-07-30 17:23:56,376 - ERROR - ログレベル:error# 2024-07-30 17:23:56,376 - CRITICAL - ログレベル:critical
のように設定レベル以上が出力される。
下記に動作確認したサンプルをのせる
loggingの使い方を整理していく。
loggingをpythonの標準ライブラリのためpipでのインストールは必要ない
loggingを使うまでの流れは下記のようになる
また設定方法としては主に
のパターンがある。
それぞれのパターンでサンプルを作ってみる。
プログラム内で直接記述する方法はシンプルで使いやすく、またプログラムなので
拡張性も高い。
サンプルとしては下記になる。
import logging# 1.ロガーを取得logger = logging.getLogger(__name__)# 2.ロガーのレベルを設定logger.setLevel(logging.DEBUG)# 3.ハンドラーの作成と設定# コンソールハンドラーを作成してロガーに追加console_handler = logging.StreamHandler()# ログレベルを設定console_handler.setLevel(logging.WARNING)# ファイルハンドラーの作成と設定file_handler = logging.FileHandler('app.log', encoding='utf-8')# ログレベルを設定file_handler.setLevel(logging.INFO) # ファイルにはINFO以上のメッセージを出力# 4.フォーマッターの作成と設定formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')console_handler.setFormatter(formatter)file_handler.setFormatter(formatter)# 5.ハンドラーをロガーに追加logger.addHandler(console_handler)logger.addHandler(file_handler)def main():# 6.ログメッセージを記録logger.debug('ログレベル:debug')logger.info('ログレベル:info')logger.warning('ログレベル:warning')logger.error('ログレベル:error')logger.critical('ログレベル:critical')if __name__ == "__main__":main()# 実行結果(コンソール)# 2024-07-30 17:23:56,375 - WARNING - ログレベル:warning# 2024-07-30 17:23:56,376 - ERROR - ログレベル:error# 2024-07-30 17:23:56,376 - CRITICAL - ログレベル:critical# 実行結果(ログふぃいる)# 2024-07-30 17:23:56,375 - INFO - ログレベル:info# 2024-07-30 17:23:56,376 - WARNING - ログレベル:warning# 2024-07-30 17:23:56,376 - ERROR - ログレベル:error# 2024-07-30 17:23:56,376 - CRITICAL - ログレベル:critical
細かく解説する
import logging# 1.ロガーを取得logger = logging.getLogger(__name__)
logging.getLoggerでloggerインスタンスを作成する。
引数はloggerを識別するために設定する。
同じ名前で作ると同じインスタンスと判定される。
逆にいうと同じ名前空間であれば同じ名前を指定すれば
同じloggerインスタンスを取得できる。
実はlogging.getLoggerでインスタンスを作らなくとも
import logginglogging.basicConfig(level=logging.DEBUG)logging.debug('This is a debug message')
のようにすればログ出力はできるのだが基本的にはしないほうがいい。
必ずlogging.getLoggerでloggerインスタンスを作成して使うこと。
logging.basicConfig()はルートロガーの設定であるため、他のloggerインスタンスにも
デフォルト設定を適用していまい、アプリケーション全体のロギング設定に影響する
# 2.ロガーのレベルを設定logger.setLevel(logging.DEBUG)
ロガーのログレベルを設定する。
設定したログレベル以上のログが出力さえるようになる。
# 3.ハンドラーの作成と設定# コンソールハンドラーを作成してロガーに追加console_handler = logging.StreamHandler()# ログレベルを設定console_handler.setLevel(logging.WARNING)# ファイルハンドラーの作成と設定file_handler = logging.FileHandler('app.log', encoding='utf-8')# ログレベルを設定file_handler.setLevel(logging.INFO) # ファイルにはINFO以上のメッセージを出力
ハンドラーは、ログメッセージをどこに出力するかを設定する。
一般的なハンドラーとして
などがある。
またハンドラーでもログレベルを設定しているのはログの出し分けを可能にするため。
下記を行っているイメージ。
DEBUGのログは記録対象にはなっているが、ハンドラーの設定がないため 出力はされない。
# 4.フォーマッターの作成と設定formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')console_handler.setFormatter(formatter)file_handler.setFormatter(formatter)
フォーマッターは、ログメッセージの出力形式を定義する。
logging.Formatterを使ってフォーマットを設定し、それをハンドラーに追加する
出力形式の詳しくは下記で
# 5.ハンドラーをロガーに追加logger.addHandler(console_handler)logger.addHandler(file_handler)
作成したハンドラーをロガーに追加する
ロガーはログメッセージを指定されたハンドラーを通じて出力するようになる。
def main():# 6.ログメッセージを記録logger.debug('ログレベル:debug')logger.info('ログレベル:info')logger.warning('ログレベル:warning')logger.error('ログレベル:error')logger.critical('ログレベル:critical')if __name__ == "__main__":main()
loggerインスタンスのメソッド(debug, info, warning, error, critical)を使って
ログメッセージを記録する。
この場合、コンソールには
のログが出力され
ファイルには
のログが出力される
INI形式のファイルにログ設定を記述し読み込む方法。
メリットとしては
な点がある。
デメリットとしては
点がある。
[loggers]keys=root,exampleLogger[handlers]keys=consoleHandler,fileHandler[formatters]keys=exampleFormatter[logger_root]level=DEBUGhandlers=consoleHandler[logger_exampleLogger]level=DEBUGhandlers=consoleHandler,fileHandlerqualname=__main__propagate=0[handler_consoleHandler]class=StreamHandlerlevel=WARNINGformatter=exampleFormatterargs=(sys.stdout,)[handler_fileHandler]class=FileHandlerlevel=INFOformatter=exampleFormatterargs=('app.log', 'a', 'utf-8')[formatter_exampleFormatter]format=%(asctime)s - %(levelname)s - %(message)sdatefmt=%Y-%m-%d %H:%M:%S
直接プログラムで書いたいてものをiniファイルに書く。
使用するロガーの名前をカンマ区切りで列挙
上記の場合はroot,exampleLoggerの二つのloggerを記載
使用するハンドラーの名前をカンマ区切りで列挙 上記の場合はconsoleHandler,fileHandlerの二つのハンドラーを記載
使用するフォーマッターの名前をカンマ区切りで列挙。
ロガーの個別設定。「logger_ロガー名」で定義する これはrootロガーの設定
exampleLoggerのロガーの設定。
qualnameはmainが指定してあるため
「logger = logging.getLogger(name)」で呼ばれた時は
このexampleLoggerが返される
「propagate=0」は親ロガーに伝播(プロパゲート)しないようにする設定。
ロガーを独立して動かすので0に設定しておく。
コンソールハンドラーの設定
ファイルハンドラーの設定
フォーマッターの定義
import loggingimport logging.config# ロギング構成ファイルを読み込むlogging.config.fileConfig('app/logging_config.ini', disable_existing_loggers=False)# __name__を使用してロガーを取得logger = logging.getLogger(__name__)def main():# ログメッセージを記録logger.debug('ログレベル:debug')logger.info('ログレベル:info')logger.warning('ログレベル:warning')logger.error('ログレベル:error')logger.critical('ログレベル:critical')if __name__ == "__main__":main()
ロギング構成ファイルを読み込んでから.loggerを取得して
実行する。
disable_existing_loggershはロギングの設定を読み込む際のオプションで、既存のロガーの状態をどうするかを指定する。
デフォルトではtrueになっていて、設定ファイルを読み込むと、すでに存在するロガーは無効化されるため
設定ファイルに含まれていないロガーはログ出力を行わなくなる。
falseにしておけばすでに存在するロガーは無効化されず、設定ファイルで指定されたロガーに加えて、
既存のロガーもそのまま使用できるのでfalseを設定しておく。
次は設定を辞書形式で定義する
メリットとしては
がある。
import loggingimport logging.config# ロギングの設定を辞書形式で定義logging_config = {'version': 1,'disable_existing_loggers': False,'formatters': {'exampleFormatter': {'format': '%(asctime)s - %(levelname)s - %(message)s','datefmt': '%Y-%m-%d %H:%M:%S'}},'handlers': {'consoleHandler': {'class': 'logging.StreamHandler','level': 'WARNING','formatter': 'exampleFormatter','stream': 'ext://sys.stdout'},'fileHandler': {'class': 'logging.FileHandler','level': 'INFO','formatter': 'exampleFormatter','filename': 'app.log','encoding': 'utf-8'}},'loggers': {'': { # ルートロガーの設定'level': 'DEBUG','handlers': ['consoleHandler', 'fileHandler']},'exampleLogger': { # 特定のロガー設定'level': 'DEBUG','handlers': ['consoleHandler', 'fileHandler'],'propagate': False}}}# ロギングの設定を適用logging.config.dictConfig(logging_config)# exampleLoggerを使用してロガーを取得logger = logging.getLogger('exampleLogger')def main():# ログメッセージを記録logger.debug('ログレベル:debug')logger.info('ログレベル:info')logger.warning('ログレベル:warning')logger.error('ログレベル:error')logger.critical('ログレベル:critical')if __name__ == "__main__":main()
辞書形式でloggerの設定を読み込む。
最後はJSONファイルを読み込んでで設定するパターン。
これはdictConfigを使う点は同じだが、
という利点がある。
{"version": 1,"disable_existing_loggers": false,"formatters": {"exampleFormatter": {"format": "%(asctime)s - %(levelname)s - %(message)s","datefmt": "%Y-%m-%d %H:%M:%S"}},"handlers": {"consoleHandler": {"class": "logging.StreamHandler","level": "WARNING","formatter": "exampleFormatter","stream": "ext://sys.stdout"},"fileHandler": {"class": "logging.FileHandler","level": "INFO","formatter": "exampleFormatter","filename": "app.log","encoding": "utf-8"}},"loggers": {"": {"level": "DEBUG","handlers": []},"exampleLogger": {"level": "DEBUG","handlers": ["consoleHandler", "fileHandler"],"propagate": false}}}
JSONでロギング設定を記載する
import loggingimport logging.configimport json# JSONファイルを読み込んでロギングの設定を適用with open('app/logging_config.json', 'r', encoding='utf-8') as f:config = json.load(f)logging.config.dictConfig(config)# ロガーを取得logger = logging.getLogger(__name__)def main():# 基本設定によるログメッセージlogger.debug('ログレベル:debug')logger.info('ログレベル:info')logger.warning('ログレベル:warning')logger.error('ログレベル:error')logger.critical('ログレベル:critical')# 動的に設定を変更dynamic_console_handler = logging.StreamHandler()dynamic_console_handler.setLevel(logging.DEBUG)dynamic_console_handler.setFormatter(logging.Formatter('%(name)s - %(levelname)s - %(message)s'))logger.addHandler(dynamic_console_handler)# 動的設定によるログメッセージlogger.debug('動的設定によるログレベル:debug')logger.info('動的設定によるログレベル:info')if __name__ == "__main__":main()# 実行結果# ログレベル:warning# ログレベル:error# ログレベル:critical# __main__ - DEBUG - 動的設定によるログレベル:debug# __main__ - INFO - 動的設定によるログレベル:info
動的にログ出力を変更できる
basicConfigを使えば、ルートロガーとそのハンドラーを
設定できる。
細かくログ出力をわけずに統一して出したいのであれば basicConfigで十分だと思う。
import logginglogging.basicConfig(level=logging.INFO,format="%(asctime)s - %(levelname)s - %(message)s",filename="app.log",filemode='a')logger = logging.getLogger("test")def main():logger.info('level:info')if __name__ == "__main__":main()# 実行結果(ファイルに出力)# 2024-08-03 01:03:31,124 - INFO - level:info
loggerは作ること。
basicConfigはログ設定を一度だけ行うための関数で、
基本的にはアプリケーション全体の最初の設定で使うのみ。
設定が一度行われた後に再度 basicConfig() を呼び出しても、設定は上書きされない。
import logginglogging.basicConfig(level=logging.INFO,format="%(asctime)s - %(levelname)s - %(message)s",filename="app.log",filemode='a')logger = logging.getLogger("test")def main():try:a =1 / 0except Exception:logger.error("error message", exc_info=True)if __name__ == "__main__":main()
exc_info=Trueを設定するだけ
実行するとapp.logに
2024-08-03 12:55:55,369 - ERROR - error messageTraceback (most recent call last):File "c:\develop\01_TechSamples\Python\PythonOrg\pythonorg-logging\app\log5.py", line 13, in maina =1 / 0~~^~~ZeroDivisionError: division by zero
のようにスタックトレースと例外情報が出力されるようになる。
Pythonのloggingの使い方の基本をまとめみた。
初期の設定方法がいろいろあるので作るアプリケーションによって
使い分ける必要がありそうだ。
またloggingはPythonのデコレーターと併用すると使いやすい。
デコレーターについては下記記事でまとめているので良ければ参考にしてください