エラー時のログを出したい
logging
モジュールにはログを出す方法がいくつかあります。
logging.debug()
logging.info()
logging.warning()
logging.error()
logging.critical()
これらの違いはログレベル(log level)によるもので、緊急度などに応じて設定しておけば「開発時は debug
も含めて出力」「本番では warn
以上(または info
以上)」のように出力するレベルを変更できます。
これと並んでもう一つ、 logging.exception()
があり、ライブラリのリファレンスには次のように書いてあります。
レベル ERROR のメッセージをこのロガーで記録します。引数は
debug()
と同じように解釈されます。例外情報がログメッセージに追加されます。このメソッドは例外ハンドラからのみ呼び出されるべきです。
https://docs.python.org/ja/3/library/logging.html#logging.Logger.exception
その名の通り、例外処理時に except:
の中で利用することを想定しています。
しかし、実装するときに勘違いしている例をよく見かけます。(私もしょっちゅうやってました)
勘違い
よくある勘違いとは「キャッチした例外を引数に入れる」というものです。
try:
int("aaa")
except Exception as e:
logging.exception(e)
これで出力されるログはこのようになります。
ERROR:root:invalid literal for int() with base 10: 'aaa'
Traceback (most recent call last):
File "main.py", line 6, in hoge
int("aaa")
ValueError: invalid literal for int() with base 10: 'aaa'
invalid literal for int() with base 10: 'aaa'
というメッセージが2回重複しているのがわかるでしょうか。
ERROR:root:
となっている部分が引数から生成されたもので、 Traceback
以降は自動的に付加された例外情報です。
リファレンスにあったのはこの部分ですね。
例外情報がログメッセージに追加されます。
引数に例外オブジェクトを渡した場合、文字列化した値 str(e)
が出力されるだけなので同じエラーメッセージが重複してしまいます。
例外情報は自動的に取得される1ので、実際には例外オブジェクトのコード上の取得は不要です。
try:
int("aaa")
except:
logging.exception("What is doing when exception happens.")
ERROR:root:What is doing when exception happens.
Traceback (most recent call last):
File "main.py", line 6, in hoge
int("aaa")
ValueError: invalid literal for int() with base 10: 'aaa'
例外だけを出力するのではなく、開発者が理解しやすいメッセージを付与するとよいでしょう。
- ログで状況を伝える
- ログメッセージを読むときに、何をしていたときに発生した例外なのか読み取れるようにする
- 再現性のための情報
-
try:
に入るときの事前条件(変数の値など)の出力をしてテストで再現できるようにする
-
-
「
except
またはfinally
節内で例外を送出 (または再送出) するとき、__context__
は自動的に最後に捕まった例外に設定されます。」 https://docs.python.org/ja/3/library/exceptions.html ↩