みなさん、何かを開発する際、途中の計算結果などが正しく処理されているかを何で確認していますか?
pythonのprint()関数のみを使用していませんか??
print()は簡単な処理や、すぐに処理結果をみたいときに大変重宝します。
しかし、何時間もスクレイピングする際や、長時間の機械学習、自動化したシステムのエラーなどをキャッチするときなどは、loggingというメソッドを用いた方が便利な場合もあります。
あと、同じログを取る方法でもいろんな選択肢を持っているほうがかっこいいと思いませんか?笑
本記事では、loggingを用いたログの取り方を学び始めたばかりの人を対象にわかりやすく解説していきます!
loggingの仕組み
筆者が最初loggingについて調べているときは、「階層構造???」「めっちゃレベル設定するじゃん」「ハンドラってなんだ?」と要領のよくない頭がオーバーヒートし、日本の気温が少し上昇しました。
少しでも気温の上昇を防ぐために、少しでもわかりやすく流れを解説していきます。
まずは、目の前で交通事故が起きたとしましょう。
ある若造がこれを観察していました。仮にこの人を伝令役としましょう。
あわてふためいたこの伝令役の若造は、近くにいた二体のロボットにそのことを伝えました。
さて、この二体のロボットはそれぞれある機能を持っていました。
1体は、伝えられた内容をパソコンの画面(コンソール)に出力する機能。
もう1体は、伝えられた内容をファイルとして形に残す機能です。
結果、事故の内容が2通りの方法で出力されました。おめでたいですね。
以上が、簡単なログ出力までの流れです。
昔話みたいな流れと、多少強引な流れはご容赦ください。
筆者が最初学んでるときは、この伝令役が直接コンソールやファイルとして出力するのかと思っていましたが、実際はそれぞれの独立した機能を持ったものがその役割を担っているんですね。
この「伝令役」「ロボット」を自分で設置し、ログを出力する流れになります。
なんでもかんでも出力するの?
ここで、皆さんがさきほどの伝令役だったとしたら、事故の内容を喋りますか??
事故が起きたら喋るけど、ヒヤリハットレベルなら時に喋らずに素通りするという方ももちろんいらっしゃると思います。
もし伝令役が喋らなかった場合、もちろんロボットにもその内容が伝わりませんので、ログは出力されません。
勘のよい方ならお気づきかもしれませんが、実際にプログラムを組む時、どの程度の重要度まで伝令役がロボットに内容を伝えるかを決めます。
例えば、「なんでもかんでもあったこと全部伝えて!」や「事故が起こった時だけ教えて!」などです。
ただ実際にはこんな抽象的な定義ではなく、5段階のレベルが決まっています。下に行くほどそれはやばい度が増します。
- DEBUG
- INFO
- WARNING
- ERROR
- CRITICAL
それぞれの詳しい内容についてはドキュメントに記載されています!
また、このレベルの設定はロボットにも設定します。
ただし、実際の事象ではなく、伝令役が伝えた内容に対してのレベル設定になります。したがって、伝令役が伝えてきた内容より下の重要度は設定できません。
例えばこんな具合です。
「レベル4以上のみロボットに伝える」という設定を伝令役にした場合、ロボットにレベル4未満の情報は伝わるはずがないので、設定できません。
なんとなくお分かりいただけたでしょうか。
また少し話は逸れますが、このロボット、どちらも用意する必要はなく、用途に合わせて必要なやつだけ用意してあげれば良いのです。
実際に書いてみる
いよいよちゃちなイラストとおさらばして具体的なコードを書いていきましょう!
loggerの設定
# 必要なライブラリのインポート
from logging import getLogger, StreamHandler, FileHandler, Formatter, INFO, ERROR
ここでは必要なライブラリたちをインポートしておきます。
続いて、logger(伝令役)をインスタンス化します!
※インスタンス化とは、実際に実体をもたせてその機能を使えるようにすつといったニュアンスです。詳しくはここでは割愛します。
logger = getLogger(__name__) # インスタンス名は 'logger'以外でもよい
__main__はモジュール名を表します。例えば、今このコードを書いているファイル名が「logger.py」だとすると、__main__の中には「__logger__」が格納されます!
モジュール(ファイル)毎にログをとるため、このように記述します。
次は、伝令役が拾いとるレベルを設定します。
logger.setLevel(INFO)
ここでは、先ほどインスタンんス化したloggerに対して、‘INFO’ 以上のレベルのログを拾うよう設定しています。
出力形式(フォーマット)の設定
今度は、ログの出力形式を設定していきます。
ログの出力内容は決まっているわけではなく、こちらで設定してあげる必要があります。
# フォーマットの設定
# [ログ取得時刻] - [ファイル名] - [ログレベル] - [メッセージ内容]
format = Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
Formatter()を用いてフォーマットを設定します!
上記で「%()s」で囲まれている箇所に決まった文言を入れることで、ログに関する様々な情報を取得することができます。
詳細に関してはこちらの「LogRecord属性」に記載されておりますので、余裕のある方は是非ご覧ください。
ここまでで、「伝令役」「伝令役が拾うログのレベル」「出力形式」を設定しました。
続いては、その内容を実際に吐き出す役目を持っているロボットたち(ハンドラ)の設定をしましょう!
ハンドラの設定
まずはコンソール上に出力するハンドラ(StreamHandler)の設定を行います。
# StreamHandler(コンソールに出力するハンドラ) のインスタンス化、追加
stream_handler = StreamHandler() # インスタンス化
stream_handler.setLevel(INFO) # ログレベルの設定
stream_handler.setFormatter(format) # 先ほど定義したフォーマットの適用
logger.addHandler(stream_handler) # 作成したloggerにハンドラを追加する
5行目の、loggerに追加する以外は先ほどのloggerのインスタンス化と同じような流れになっています。
また、今回のStreamHandlerのログレベルは ‘INFO’ に設定してあります。
同様に、今度はファイル形式でログを出力するハンドラ(FileHandler)の設定を行います。
# FileHndler(ファイルに出力するハンドラ)のインスタンス化、追加
file_handler = FileHandler('logfile.log') # インスタンス化、引数に出力ファイルパスを指定
file_handler.setLevel(ERROR) # ログレベルの設定
stream_handler.setFormatter(format) # 先ほど定義したフォーマットの適用
logger.addHandler(file_handler) # 作成したloggerにハンドラを追加する
StreamHandlerと異なり、出力するファイルのパスを指定してあげる(2行目)必要があります。
また、今回のFileHandlerのログレベルを ‘ERROR’ に設定しました。
これでログを出力させる準備はできましたので、実際に出力させてみましょう!
ログを出力してみる
まずここまでの状況を整理してみましょう。
- loggerをインスタンス化(ログレベル : INFO)
2. 出力形式(フォーマット)を設定
3. StreamHandler(コンソール出力)を設定(ログレベル : INFO)
4. FileHandler(ファイル出力)を設定(ログレベル : ERROR)
また、これまでのコードを全部記載します。(※21〜23行目追加)
from logging import getLogger, StreamHandler, FileHandler, Formatter, INFO, ERROR
logger = getLogger(__name__)
logger.setLevel(INFO)
format = Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') # フォーマットの内容
# StreamHandler(コンソールに出力するハンドラ) のインスタンス化、追加
stream_handler = StreamHandler() # インスタンス化
stream_handler.setLevel(INFO) # ログレベルの設定
stream_handler.setFormatter(format) # 先ほど定義したフォーマットの適用
logger.addHandler(stream_handler) # 作成したloggerにハンドラを追加する
# FileHndler(ファイルに出力するハンドラ)のインスタンス化、追加
file_handler = FileHandler('logfile.log') # インスタンス化、引数に出力ファイルパスを指定
file_handler.setLevel(ERROR) # ログレベルの設定
stream_handler.setFormatter(format) # 先ほど定義したフォーマットの適用
logger.addHandler(file_handler) # 作成したloggerにハンドラを追加する
# 以下、info, errorログを発生させる
logger.info('This is Information.')
logger.error('This is error!!!!!!!')
このファイルを実行してみましょう!
まず実行後のコンソール画面。
2022-07-18 12:43:13,529 - __main__ - INFO - This is Information.
2022-07-18 12:43:13,529 - __main__ - ERROR - This is error!!!!!!!
続いてファイル出力された中身。
This is error!!!!!!!
2022-07-18 12:43:13,529 - __main__ - ERROR - This is error!!!!!!!
ここからお分かりのように、FileHandlerのログレベルは ‘ERROR’ なので、’INFO’ に関するログは出力されていません。
また、今回はどちらのハンドラも共通のフォーマットを設定していますが、それぞれ個別に設定することもできます!
コンソールで見たい情報、ファイルとして残しておきたい情報など用途に合わせて適宜設定してみると良いでしょう。
まとめ
今回は、pythonのloggerを用いてログを出力させるための流れをなるべくシンプルに紹介させていただきました。
フォーマットの設定や、ハンドラの種類などは、一連の流れを学んだ次の段階だと思うので、今回はその辺についてはあまり触れずに進めていきました。
詳細な設定などに関しては今後機会がありましたら追加していく予定ですので、よかったらご覧いただけると幸いです!
以上、loggerの紹介でした!
ご覧いただきありがとうございました。
コメント
コメント一覧 (4件)
Hey very interesting blog!
Thanks!
[…] 【python】print()は不要!?loggerでログを出力する方法をわかりやすく解説! […]
[…] 【python】print()は不要!?loggerでログを出力する方法をわかりやすく解説! […]