概要
Pythonのloggingを使っていると色々なオブジェクトやメソッドが出てくるなぁと思ったので、それぞれサンプルコードで整理してまとめました。
サンプルコード4種の紹介
以下4パターンを紹介します。
-
loggingのみの場合 -
loggerを使う&FileHandlerを明示的に作成する場合 -
basicConfigの外でFileHandlerを明示的に作成する場合 -
FileHandlerとStreamHandlerを利用しそれぞれにログレベルを設定する場合
1: loggingのみの場合
まずはloggingのみの場合。
ログファイルの保存先は、日時がファイル名に残るように設定されたファイルにしています。
import logging
import os
from datetime import datetime
# ログの保存先とフォーマットを設定
log_filename = 'loggings/log_file_' + datetime.now().strftime("%Y-%m-%d") + '.log'
if not os.path.exists("loggings"):
os.makedirs("loggings")
log_format = '%(asctime)s,%(msecs)d | %(levelname)s | %(name)s - %(message)s'
logging.basicConfig(filename=log_filename,
level=logging.DEBUG,
format=log_format,
encoding='utf-8',
datefmt='%Y-%m-%d %H:%M:%S')
# ログメッセージを残す
logging.debug('loggingのみの場合のDebug Messageです')
出力結果:
2023-08-30 21:22:53,930 | DEBUG | root - loggingのみの場合のDebug Messageです
logging.basicConfig関数とは?
logging.basicConfigは、loggingモジュールの関数で、ロギングの基本的な動作をカスタマイズできます。一度のみ呼び出されます。最低でもlevel(ログレベル)が必要です。
logging.basicConfig(level=logging.DEBUG)
実際に運用する際は、記録として残すことが多いと思うので、上記のようにfilenameで指定してあげます。また、出力結果にあるように
日時 | ログレベル | ロガー名
といった時間形式にしたい場合は、formatで指定します。
2: loggerを使う&FileHandlerを明示的に作成する場合
import logging
import os
from datetime import datetime
# ログの保存先とフォーマットを設定
log_filename = 'loggings/log_file_' + datetime.now().strftime("%Y-%m-%d") + '.log'
if not os.path.exists("loggings"):
os.makedirs("loggings")
log_format = '%(asctime)s,%(msecs)d | %(levelname)s | %(name)s - %(message)s'
logging.basicConfig(handlers=[logging.FileHandler(
filename=log_filename,
encoding='utf-8',
mode='a')],
format=log_format,
datefmt='%Y-%m-%d %H:%M:%S',
level=logging.DEBUG)
logger = logging.getLogger('Apple')
logger.info("AppleロガーのInfo Messageです")
出力結果:
2023-08-30 21:24:36,423 | INFO | Apple - AppleロガーのInfo Messageです
FileHandlerオブジェクトとは?
1番目のものからFileHandlerを明示的に作成しました。1番目のものでも、FileHandlerを使用していますが、明示的に作成する場合は上記のようにします。
明示的に使用すると、フォーマットやログレベルなどの設定をハンドラーごとにカスタマイズしやすくなります(ここの具体的なコードは後述します)。
ハンドラーとは、ログメッセージの出力先や挙動を制御するためのコンポーネントのことです。
1つのロガーに対して、ハンドラは複数指定できる、というのがポイントです。
modeパラメータとは?
ちなみに、modeパラメータは、FileHandlerで指定する必要があります。
logging.basicConfigの引数に入れると、以下のエラーになります。
ValueError: Unrecognised argument(s): mode
このmodeパラメータは、ファイルを開く際のモードを指定するための引数です。modeパラメータに指定できる主な値は以下の通り。
・'w': 書き込みモード。ファイルが存在する場合は上書きされます。
・'a': 追記モード。ファイルが存在する場合、既存の内容の末尾に追記されます。
・'x': 排他的な書き込みモード。ファイルが存在する場合はエラーとなります。
getLogger関数とは?
また、loggerの使用を追記しています。これは、loggingモジュール内のクラスであり、アプリケーション内で特定の名前を持つロギングインスタンスを作成するために使用されます。logging.getLoggerで特定のロガー名を指定することができます。
3: basicConfigの外でFileHandlerを明示的に作成する場合
2番目のものでは、logging.basicConfigの中にFileHandlerがあって可読性がちょっと低いですね。。ということで、basicConfigの外に定義します。
import logging
import os
from datetime import datetime
# ログの保存先とフォーマットを設定
log_filename = 'loggings/log_file_' + datetime.now().strftime("%Y-%m-%d") + '.log'
if not os.path.exists("loggings"):
os.makedirs("loggings")
log_format = '%(asctime)s,%(msecs)d | %(levelname)s | %(name)s - %(message)s'
# ファイルハンドラーを定義
file_handler = logging.FileHandler(filename=log_filename, encoding='utf-8', mode='a')
formatter = logging.Formatter(fmt=log_format, datefmt='%Y-%m-%d %H:%M:%S')
file_handler.setFormatter(formatter)
# ルートロガーの設定
logging.basicConfig(handlers=[file_handler], level=logging.DEBUG)
logger = logging.getLogger('Banana')
logger.warning("BananaロガーのWarning Messageです")
出力結果:
2023-08-30 21:26:02,550 | WARNING | Banana - BananaロガーのWarning Messageです
ちなみに、ただ外に定義するだけでは以下のエラーになります。
TypeError: 'FileHandler' object is not iterableとなる
外に定義すると、このようにFormatterとかsetFormatterが必要になります。
Formatterオブジェクトとは?
ログメッセージの表示形式を制御するために使われます。
日付、レベル、メッセージなどの要素を含むログエントリのフォーマットを指定できます(上記でいうlog_formatの内容)。
Formatterのフォーマット文字列内にはプレースホルダーを使用することが可能です。その場合、上記コードのように変数を渡すのが一般的かなと思います。(上記fmt=は省略可能)
ちなみに、logging.Formatterのコンストラクタに指定できる引数は表示フォーマットのほか、以下のものがあります。
・fmt: 表示フォーマット
・datefmt: 日付フォーマット
・style: フォーマット文字列のスタイル
・validate: バリデーション
・defaults: デフォルト値
setFormatterメソッドとは?
HandlerにFormatterを設定するために使用されます。
4: FileHandlerとStreamHandlerを利用しそれぞれにログレベルを設定する場合
最後に、FileHandlerを明示的に作成するメリットを加えます。
import logging
import os
from datetime import datetime
# ログの保存先とフォーマットを設定
log_filename = 'loggings/log_file_' + datetime.now().strftime("%Y-%m-%d") + '.log'
if not os.path.exists("loggings"):
os.makedirs("loggings")
log_format = '%(asctime)s,%(msecs)d | %(levelname)s | %(name)s - %(message)s'
# ロガーを作成
logger = logging.getLogger('Lemon')
logger.setLevel(logging.DEBUG) # ログレベルを設定
# ファイルハンドラーを作成してロガーに追加
file_handler = logging.FileHandler(filename=log_filename, encoding='utf-8', mode='a')
file_handler.setLevel(logging.DEBUG) # ファイルハンドラーのログレベルを設定
formatter = logging.Formatter(log_format, datefmt='%Y-%m-%d %H:%M:%S')
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
# コンソールハンドラーを作成してロガーに追加。コンソールに出力するStreamHandlerを利用
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO) # コンソールハンドラーのログレベルを設定
console_formatter = logging.Formatter(log_format, datefmt='%Y-%m-%d %H:%M:%S')
console_handler.setFormatter(console_formatter)
logger.addHandler(console_handler)
# ログメッセージを残す
logger.debug('Debugレベルのメッセージ')
logger.info('Infoレベルのメッセージ')
出力結果:
2023-08-30 21:26:42,598 | DEBUG | Lemon - Debugレベルのメッセージ
2023-08-30 21:26:42,598 | INFO | Lemon - Infoレベルのメッセージ
上記のコードでは、loggerに対してaddHandlerメソッドを使用して、ファイルハンドラーとコンソールハンドラーをそれぞれ追加しています。
・ファイルハンドラー:DEBUGレベルのメッセージを受け付け、ログファイルに書き込む
・コンソールハンドラー:INFOレベルのメッセージを受け付け、コンソールに表示する
addHandlerメソッドとは?
addHandlerメソッドは、特定のロガーにハンドラー(ログの出力先)を追加するために使用されます。これにより、同一のロガーに複数のハンドラーを追加して、異なる出力先にログメッセージを送信することができます。
上記の例で言えば、ログファイルとコンソール(ターミナル)に表示することができました。
setLevelメソッドとは?
ロガー(Logger)やハンドラー(Handler)のログレベルを設定するために使われます。どちらにも使えます。
これにより、特定のログレベル以上のメッセージだけを記録するように設定できます。不要なログメッセージをフィルタリングする時に役立ちますね。
今更ですが、ログレベルは以下の重要度の順位で定義されています。
CRITICAL: 重大なエラーや致命的な問題
ERROR: エラー
WARNING: 警告や注意
INFO: 情報メッセージ
DEBUG: デバッグ情報
NOTSET: デフォルトの状態