SikuliXなどを扱う上で「どこで止まってしまったか?」「どんな処理をしたか?」などの情報が欲しいことはないでしょうか?
もちろん、SikuliXに関わらず、Pythonによる開発を行っている場合は、なおさらです。
今回は、そんな時に役立つPythonの「ロギング機能」についてまとめます。
基礎知識
まず、本題に行く前に、ぜひ理解しておいてほしい用語を以下にまとめます。
ロガー
ログ出力を行うもののことです。
Pythonでは、Loggerオブジェクトが相当します。
ロギング
ログを出力すること。
ある処理の前後でログを出力させるようにしたり、エラーメッセージの出力もロギングの1つと言えます。
広い意味で言えば、製品のユーザーにエラー内容を出力させるのも含まれると思います。
しかし、一般的には、プログラム開発者が「障害解析などの際に必要な情報を出力させておくこと」を意味するでしょう。
ログ
発生した事象の記録のこと。
不具合(障害)が発生した時の原因究明(解析)などに役立ちます。ソフトウェアによる製品開発をする上では、必須のものになります。
ログレベル
ログの種類の区分けです。
ログと言っても、情報程度の内容から、障害解析に関わるものまで様々なので、区分けをしておくとフィルタをかけることにもなり、非常に便利です。いざ出力するとなった時に、「どのレベルからログとして残すか」「どのレベルのログをどのような形式でどこに出力するか?」を選択することが出来ます。
Pythonのロギング機能では、以下のような種類を設定できます。
名称 | 設定値 | 主な役割 | 備考 |
NOTSET | 0 | 「設定値なし」で、親の設定を引き継ぐ | 最下位のレベル |
DEBUG | 10 | デバッグ用 | ーー |
INFO | 20 | 正常動作用 | ーー |
WARNING | 30 | 不正動作への警告用 | デフォルト値 |
ERROR | 40 | エラー用 | ーー |
CRITICAL | 50 | 緊急性の高い致命的な問題用 | 最上位のレベル |
「NOTSET」を設定した場合については、親の設定を変更していないとデフォルト値である「WARNING」が適用されます。
明示的にログレベルを設定しない場合も、デフォルト値である「WARNING」が適用されます。
ハンドラー
一般的には、「ユーザーやOSなどからの要求に対応するためのプログラムモジュール」と指します。
プログラムを起動する要求に対応する「起動用モジュール」、割り込みを処理する「割り込み用のモジュール」などは「ハンドラー」と言えます。
Pythonのロギング用のハンドラーは、以下のようなものがあります。
名称 | 役割 |
StreamHandler | コンソールに出力する |
FileHandler | ファイルに出力する |
SMTPHandler | SMTPを介して、メールとして送信する |
HTTPHandler | GET または POST などのHTTPメソッドを使って Webサーバに送信する |
※より詳細な情報や他のハンドラーは、こちらをご覧ください。
基本的な使い方
ここでは、基本的な手順と、それを実行するためのソースコードの一例を交えて、説明していきます。
基本的なフロー
実施することは、以下のようなことになります。
- loggingモジュールをインポートする
- ロガーの作成する
- 作成したロガーに、ログレベルを設定する
- ログを出力する際のフォーマットを設定する
- ログをハンドラーに設定する
- ⑤のハンドラーに、出力するログレベルを設定する
- ⑤のハンドラーに、④で設定した出力フォーマットを設定する
- ⑤のハンドラーを、②のロガーに設定する
- ログとして出力したいメッセージを、出力したいソースコードの場所に記載する。
ソースコード
以下は、上記「基本的なフロー」に記載したものをソースコードに書き起こしたものです。各番号は、上記の説明と一致しています。
なお、ログを出力する際は、ファイル出力を行う内容になっています。
- import logging
- logger = logging.getLogger(‘sampleLogger’)
- logger.setLevel(logging.DEBUG)
- formatter = logging.Formatter(‘%(asctime)s[%(levelname)s]%(message)s’)
- file_handler = logging.FileHandler(“sampleLog” + datetime.datetime.now().strftime(‘%y%m%d’) + “.log”)
- file_handler.setLevel(logging.DEBUG)
- file_handler.setFormatter(formatter)
- logger.addHandler(file_handler)
- logger.error(u”エラー用の文”)
ソースコードの説明
ここでは、上記の「基本的なフロー」と「ソースコード」の内容を説明していきます。
①:loggingモジュールをインポート
Pythonでは、ロギング用のモジュールを別途インストールするようなことはせずとも、標準機能で備わっています。
そのため、ロギング機能を使用する際は、単に「import logging」でインポートすれば良いです。
②:ロガーオブジェクトの作成
「ロガーの作成」を行うと、モジュール毎にロガーを使い分けることができます。そのため、汎用性も高くなるので「ロガーの作成」を行うことをお勧めします。
ロガーの作成時は「getlogger」を使用して作成しています。( )内に記載の引数は、ロガーオブジェクトの名称です。
なお、以下のように記述すれば、複数のモジュール間で1つのロガーを使いまわすことも可能です。
【使いたいロガー】
import logging
def “関数名”:
logger = logging.getLogger(“ロガー名”)
~中略:ロガーの各種設定~
return logger
【使用したいモジュール内】
from <“ロガー名”> import “関数名”
logger = “関数名”(“使用したいモジュール内でのロガー名”)
③および⑥:ログレベルの設定
ログレベルを設定することで、この内容に沿ったログが出力されるようになります。構文は以下です。
logger.setLevel(logging.”設定したいログレベル”)
なお、2度ログレベルを設定していますが、以下のような違いがあります。
設定場所 | ログへの影響 |
③:logger | ロガーが保有し、データとして扱えるログレベルを設定します。 そのため、ここで設定したログレベルよりも「下位」のレベルを持つログは扱うことができません。 |
⑥:handler | handlerが実際に出力するログレベルを設定します。 そのため、③:loggerで設定したログレベルと「同じ」もしくは「高位」のレベルを設定する必要があります。 |
④:フォーマットの設定
ロガーで出力するログのフォーマットを設定しています。構文は以下です。
“フォーマットの名称” = logging.Formatter(“設定したいフォーマット”)
フォーマットでは、以下のようなショートコードを使うことができます。
フォーマット | 役割 |
%(asctime)s | 実行時刻 |
%(filename)s | ファイル名 |
%(funcName)s | 行番号 |
%(levelname)s | ログの定義 |
%(lineno)d | ログレベル名 |
%(message)s | ログメッセージ |
%(module)s | モジュール名 |
%(name)s | 関数名 |
%(process)d | プロセスID |
%(thread)d | スレッドID |
なお、ショートコードと共に、任意の文字を入力することも可能です。上記のサンプルコードでは、「%(levelname)s」の両脇にある「[」「]」が相当します。
⑤:ハンドラーの設定
ハンドラーを使用し、ファイル出力をするための設定をしています。設定したいハンドラー名(例では、”file_handler”)に代入する形になります。
ファイル出力(FileHandler)の場合の引数は、出力する際のファイル名(拡張子あり)です。
コンソールに出力したい場合は、「StreamHandler」を用います。引数は、標準出力を示す「sys.stdout」を用い、「logging.StreamHandler(sys.stdout)」とするのが良いでしょう。
⑦:フォーマットの適用
⑤で設定したハンドラーに、出力する際のフォーマットを適用しています。構文は、以下です。
“⑤で設定したハンドラー名”.setFormatter(formatter)
⑧:ハンドラーの適用
ロガーに、設定したハンドラーを設定することで、実際にログを出力されるようになります。構文は以下です。
logger.addHandler(“⑤で設定したハンドラー名”)
複数のハンドラーを同時に動かす際は、この「addHandler」でロガーに追加することで対応できます。コンソール出力とファイル出力を同時に行う場合などに便利でしょう。
⑨:出力するログの内容の記載
ログの内容の記載は、処理が通過したら出力させたい任意の場所に記載することで対応できます。
以下のように、出力したいログレベルに対応して記載することが可能です。
ログレベル | 記載例 |
DEBUG | logger.debug(u”デバッグ用の文”) |
INFO | logger.info(u”Info用の文”) |
WARNING | logger.warning(u”警告用の文”) |
ERROR | logger.error(u”エラー用の文”) |
なお、以下のような変数を使用しても対応可能です。
errorMsg = u”エラー用の文”
logger.error(errorMsg)
まとめ
Pythonによるログ出力のための機能:ロギングについて、まとめてみました。
SikuliXなどでエラーが発生した時など、Pythonによるコーディングを行う時は必須のものと言えると思います。
ログ出力したいときは、ぜひ参考にしてみてください。