Python Logging – Simplic Guide with Full Code and Examples
Loggingモジュールは、コードの実行を監視し、コードがクラッシュしたときにログをチェックしてその原因を特定できるようにします。 ログ メッセージには、デバッグ メッセージ、情報メッセージ、警告メッセージ、エラー メッセージ、クリティカル メッセージといった階層構造が組み込まれています。 トレースバック情報も含めることができます。 複数のモジュールを持つ小規模から大規模なpythonプロジェクト向けに設計されており、あらゆるモジュール式のpythonプログラミングに強く推奨されています。
Logging in Python – Simplified Guide with Full Code and Examples.この記事では、loggingモジュールの使い方をシンプルでわかりやすく説明しています。 Photo by Andrea Reiman.
コンテンツ
-
- なぜloggingなのか?
- 基本的なロギングの例
- ロギングの5つのレベル
- コンソールの代わりにファイルにロギングする方法
- ロギングのフォーマットを変更する方法
- すべてのモジュールのためにルートロガーで作業することが最良のアイデアではない理由
- 新しいロガーを作成する方法
- ファイルハンドラとフォーマッタとは何か、どのように設定するか?
- ログに記録されたメッセージにトレースバック情報を含める方法
- 練習問題
- 結論
なぜログを記録するのか?
Python スクリプトを実行していると想像してみてください。スクリプトのどの部分が実行されたのか、どの時間に実行されたのか、場合によっては変数がどのような値を持っているのかを調べたいと思います。
通常は、意味のあるメッセージを ‘print()
‘ で出力してコンソールで見ることができます。
問題は、多くのモジュールを含む大規模なプロジェクトでこのアプローチを使用する場合、より構造的で堅牢なアプローチが必要だということです。
なぜなら、コードは開発、デバッグ、レビュー、テスト、または生産に至るまでさまざまな段階を経る可能性があるため、印刷されるメッセージにはさまざまなレベルの詳細を見たいと思うからです。
イメージとしては、if else
print
のステートメントだけで、これらをすべて行うことができます。
さらに、印刷されるメッセージには一定の階層が必要です。
例えば、ある「テスト」の実行中には、警告とエラーメッセージだけを表示したいとします。 一方、「デバッグ」では、デバッグに役立つより詳細なメッセージが必要です。
これらの問題はすべて、logging
モジュールによってうまく解決されています。
ロギングを使用すると、以下のことが可能になります。
- メッセージレベルを制御して、必要なものだけをログに記録する
- ログを表示または保存する場所を制御する
- 内蔵のメッセージテンプレートを使って、ログのフォーマットを制御する
- INFO: 期待通りに動作しているか確認する。 Value=20.
- WARNING:予期せぬことが起こった、または何らかの問題を示している。 しかし、ソフトウェアは期待通りに動作しています。 Value=30.
- ERROR: より深刻な問題で、ソフトウェアがある機能を実行できません。 値=40
- CRITICAL: 深刻なエラーで、プログラム自体が実行を継続できない可能性があります。 Value=50
ilogging
が便利なのはわかったけど、専門的すぎてちょっと難しそう」とおっしゃるかもしれません。
早速ですが、この記事の目的は、ロギングを簡単に学べるようにすることです。
基本的なロギングの例
Pythonには、python標準ライブラリの一部である内蔵のlogging
モジュールが用意されています。
ロギングを使用するには、を使用して基本的な設定を行うだけです。 実は、これもオプションです。
そして、print()
logging.{level}(message)
を呼び出して、メッセージをコンソールに表示します。
import logginglogging.basicConfig(level=logging.INFO)def hypotenuse(a, b): """Compute the hypotenuse""" return (a**2 + b**2)**0.5logging.info("Hypotenuse of {a}, {b} is {c}".format(a=3, b=4, c=hypotenuse(a,b)))#> INFO:root:Hypotenuse of 3, 4 is 5.0
表示されるログメッセージは、デフォルトで次のような形式になっています。
上記の場合、レベルはinfo
logging.info()
を呼び出したためです。
ロガーは root
と呼ばれていますが、これはデフォルトのロガーであり、まだ新しいロガーを作成していないからです。
しかし、そもそもロガーとは何でしょうか。
ロガーとは、さまざまなタイプや形式のメッセージを記録するために作成および設定できるエンティティのようなものです。
コンソールに印刷するロガーと、ログをファイルに送信し、異なるロギング レベルを持ち、特定のモジュールに固有のロガーを設定することができます。
最後に、メッセージは logging.info()
に渡した文字列です。
さて、もし logging.basicConfig(level=logging.INFO)
を設定していなかったら、どうなっていたでしょうか
答え。
なぜですか?
それを知るために、ログのレベルを理解しましょう。
ロギングの 5 つのレベル
logging
には、与えられたロガーが設定できるログの 5 つの異なる階層的なレベルがあります。
各レベルについて python のドキュメントに書かれていることを見てみましょう。 問題を診断するための詳細な情報です。 Value=10.
さて、先ほどの例で logging.basicConfig(level=logging.INFO)
を設定しなかったらどうなっていたかという疑問に戻ります。
つまり、logging.warning()
以上のレベルのメッセージのみがログに記録されるということです。
だからこそ、最初はINFO
logging.basicConfig(level=logging.INFO)
内)。
もしレベルをlogging.ERROR
logging.error
logging.critical
のメッセージだけがログに記録されます。 いいですか?
import logginglogging.basicConfig(level=logging.WARNING)def hypotenuse(a, b): """Compute the hypotenuse""" return (a**2 + b**2)**0.5kwargs = {'a':3, 'b':4, 'c':hypotenuse(3, 4)}logging.debug("a = {a}, b = {b}".format(**kwargs))logging.info("Hypotenuse of {a}, {b} is {c}".format(**kwargs))logging.warning("a={a} and b={b} are equal".format(**kwargs))logging.error("a={a} and b={b} cannot be negative".format(**kwargs))logging.critical("Hypotenuse of {a}, {b} is {c}".format(**kwargs))#> WARNING:root:a=3 and b=3 are equal#> ERROR:root:a=-1 and b=4 cannot be negative#> CRITICAL:root:Hypotenuse of a, b is 5.0
コンソールの代わりにファイルにログを記録する方法
ルートロガーからファイルにログメッセージを送信するには。
import logginglogging.basicConfig(level=logging.INFO, file='sample.log')
これで、以降のすべてのログ メッセージは、現在の作業ディレクトリにあるファイル「sample.log」に直接送信されます。log」に送られます。
ログ形式の変更方法
loggingモジュールは、ログメッセージに様々な詳細情報を追加するための省略記号を提供します。
ログメッセージのフォーマットを変更して、TIME、LEVEL、MESSAGEを表示するようにしてみましょう。
import logginglogging.basicConfig(level=logging.INFO, format='%(asctime)s :: %(levelname)s :: %(message)s')logging.info("Just like that!")#> 2019-02-17 11:40:38,254 :: INFO :: Just like that!
6. すべてのモジュールでルート ロガーを使用することが最善のアイデアではない理由
すべてのモジュールが同じ「ルート」ロガーを共有するからです。
それがなぜ悪いのか、以下のコードを見てみましょう:
# 1. code inside myprojectmodule.pyimport logginglogging.basicConfig(file='module.log')#-----------------------------# 2. code inside main.py (imports the code from myprojectmodule.py)import loggingimport myprojectmodule # This runs the code in myprojectmodule.pylogging.basicConfig(file='main.log') # No effect, because!
プロジェクトに 1 つ以上のモジュールがあるとします。 そして、これらのモジュールは基本的なルートモジュールを使用しています。
いったん設定されると、メイン ファイルのルート ロガー (「myprojectmodule
」モジュールをインポートしたもの) は、ルート ロガーの設定を変更できなくなります。
つまり、myprojectmodule
からのメッセージをあるファイルに、メインモジュールからのログを別のファイルに記録したい場合、ルートロガーではそれができないということです。
そのためには、新しいロガーを作成する必要があります。
新しいロガーの作成方法
新しいロガーは、「logger.getLogger(name)
」メソッドを使用して作成できます。 同じ名前のロガーが存在する場合は、そのロガーが使用されます。
ロガーにはどんな名前でもつけることができますが、慣習的には次のように __name__
変数を使用します:
logger = logging.getLogger(__name__)logger.info('my logging message')
しかし、なぜ名前をハードコードするのではなく、ロガーの名前として __name__
を使用するのでしょうか?
それは、__name__
という変数に、そのコードを呼び出したモジュール(pythonファイル)の名前が入るからです。
こうすることで、将来的にモジュール名(ファイル名)を変更することになっても、内部のコードを修正する必要はありません。
さて、新しいロガーを作成したら、ルートの logging.info()
logger.info()
を使用してすべてのメッセージをログに記録することを覚えておく必要があります。
注意すべきもう 1 つの側面は、すべてのロガーには組み込みの階層があるということです。
この場合、カスタム ロガーはフォールバックして、ルート ロガー自身によって設定されたファイルに書き込みます。
では、ファイル ハンドラーとは何か、どのように設定するのか
ファイル ハンドラーおよびフォーマッターとは何か、どのように設定するのか。
FileHandler()
Formatter()
クラスは、ルート ロガー以外のロガーの出力ファイルとメッセージの形式を設定するために使用されます。
先ほど、ルートロガー(の中)のメッセージのファイル名と形式を設定したことを覚えていますか?
filename
format
のパラメーターを指定しただけで、その後のログはすべてそのファイルに送られました。
しかし、別のロガーを作成する場合は、logging.FileHandler()
logging.Formatter()
オブジェクトを使って個別に設定する必要があります。
import logging# Gets or creates a loggerlogger = logging.getLogger(__name__) # set log levellogger.setLevel(logging.WARNING)# define file handler and set formatterfile_handler = logging.FileHandler('logfile.log')formatter = logging.Formatter('%(asctime)s : %(levelname)s : %(name)s : %(message)s')file_handler.setFormatter(formatter)# add file handler to loggerlogger.addHandler(file_handler)# Logslogger.debug('A debug message')logger.info('An info message')logger.warning('Something is not right.')logger.error('A Major error has happened.')logger.critical('Fatal error. Cannot continue')
フォーマッターを「file_handler
logger
」には直接設定していないことに注目してください。
上記のコードがメインプログラムから実行されたと仮定して、作業ディレクトリの中を見ると、logfile.log
という名前のファイルが存在しなければ作成され、以下のようなメッセージが表示されます。
#> 2019-02-17 12:40:14,797 : WARNING : __main__ : Something is not right.#> 2019-02-17 12:40:14,798 : ERROR : __main__ : A Major error has happened.#> 2019-02-17 12:40:14,798 : CRITICAL : __main__ : Fatal error. Cannot continue
繰り返しになりますが、Formatter
FileHandler
オブジェクトに設定されており、直接ロガーに設定されているわけではありません。 これに慣れる必要があるかもしれません。
ログに記録されたメッセージにトレースバック情報を含める方法
「debug
info
warning
error
critical
‘ のメッセージでは、関連するトレースバック情報を含む例外を記録することができます。
logger.exception
logger.exception
は、引数で与えられたメッセージとエラーメッセージのトレースバック情報を記録します。
import logging# Create or get the loggerlogger = logging.getLogger(__name__) # set log levellogger.setLevel(logging.INFO)def divide(x, y): try: out = x / y except ZeroDivisionError: logger.exception("Division by zero problem") else: return out# Logslogger.error("Divide {x} / {y} = {c}".format(x=10, y=0, c=divide(10,0)))#> ERROR:__main__:Division by zero problem#> Traceback (most recent call last):#> File "<ipython-input-16-a010a44fdc0a>", line 12, in divide#> out = x / y#> ZeroDivisionError: division by zero#> ERROR:__main__:None
Exercises
- 新しいプロジェクト ディレクトリと ‘
example.py
という名前の新しい python ファイルを作成します。 loggingモジュールをインポートし、ルートロガーを「debug」メッセージのレベルに設定します。 info」メッセージをテキストで記録します。 “ - メッセージ「This is root logger’s logging message!」を次のようにフォーマットするように、ルートロガーを構成します。”
#> 2019-03-03 17:18:32,703 :: INFO :: Module <stdin> :: Line No 1 :: This is root logger's logging message!
解決策の提示
import logginglogging.basicConfig(level=logging.INFO, format='%(asctime)s :: %(levelname)s :: Module %(module)s :: Line No %(lineno)s :: %(message)s')logging.info("This is root logger's logging mesage!")
- 同じディレクトリに「
mymod.py
」という名前の別のpythonファイルを作成し、モジュールの名前を持つ新しいロガーを作成します。 これを「エラー」メッセージのレベルに設定し、ログ出力を「mymod_{current_date}.log」というファイルに送信するようにします。 - 上記で作成した「mymod」ロガーから、次のような「クリティカル」メッセージを上記のログファイルに記録します。 “This is a critical message!
結論
練習問題を解くことができた方、おめでとうございます!
とても便利でわかりやすかったですよね。
Happy logging!
Pythonベースのプロジェクトに取り組む際には、logging
モジュールを試してみることを忘れないでください。