概要
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: デフォルトの状態