Python の logging フォーマットの出力例(10種類以上)

ロギング logging の セットフォーマッター .setFormatter(fmt) で指定可能な、10種類以上のフォーマットの出力例です。

Python で『どのようなログ表示が出来るのか?』を確認するために、コードを書きました。

フォーマットの種類は、Python マニュアルの『LogRecord 属性』に載っていたものを試しました。

Python マニュアル
LogRecord 属性(ログレコード属性)

日付、時刻、関数名などのほかに、行番号やプロセス ID まで、自動でログに出力することができました。

それらの実行結果を紹介します。

スポンサーリンク

実行結果

『test_log_formats.py』という Python コードを書いて、以下の場所で実行しました。そのときのログ出力(ログ表示)です。

コマンドプロンプト
F:\apps\data>python "F:\apps\data\test_log_formats.py"

ストリームハンドラのログ出力(標準出力)

ストリームハンドラ logging.StreamHandler() のログ出力です。

format の種類は、Python マニュアルの LogRecord 属性 にあったものを使いました。

start《 LogRecord 属性の出力テスト 》
[ format ]          -> [ 出力 ] -> [ 説明 ]
%(asctime)s         -> 2019-11-03 13:59:56,644 -> 時刻 (人が読める形式)
%(created)f         -> 1572757196.645207 -> 時刻 (time.time()形式)
%(filename)s        -> test_log_formats.py -> ファイル名
%(funcName)s        -> test_formats -> 関数名
%(levelname)s       -> DEBUG -> ロギングレベルの名前
%(levelno)s         -> 10 -> ロギングレベルの数値
%(lineno)d          -> 81 -> 行番号
%(module)s          -> test_log_formats -> モジュールの名前
%(msecs)d           -> 646 -> 時刻のミリ秒部分 (milliseconds)
%(name)s            -> __main__ -> ロガーの名前
%(pathname)s        -> F:\apps\data\test_log_formats.py -> ファイルパス
%(process)d         -> 8676 -> プロセスID (PID)
%(processName)s     -> MainProcess -> プロセス名
%(relativeCreated)d -> 5 -> logging モジュール読み込みからの時刻 (ミリ秒)
%(thread)d          -> 4788 -> スレッドID
%(threadName)s      -> MainThread -> スレッド名
end

ファイルハンドラのログ出力(ファイル)

ファイルハンドラ logging.FileHandler() のログ出力です。

ログファイル『format.log』の内容ですが、上記じょうきストリームハンドラのものと同じでしたので、省略します。

もし、ログ出力の実験などで、ストリームとファイルで内容をちがえたいときは、それぞれに sh.setFormatter('%(message)s sh')fh.setFormatter('%(message)s fh') のような目印を追加します。

これで、末尾の文字列 sh, fh から、ハンドラを見分けることが出来ました。

ほかにも、片方のデバッグレベルを変更して、ログを片方にだけ出す、といった方法がありました。

Python コード

ログのフォーマットをすべて試すために書いた Python コードです。

コードの説明は、次のせつの『Python マニュアルの場所』に書きました。

"""ログのフォーマットをすべて試すコード例"""

import pathlib
import logging

def main():
    """メイン関数"""

    # フォルダ/ファイル 設定
    data_dir = pathlib.Path(r'F:\apps\data')
    format_log = data_dir.joinpath('format.log')

    # ロガー作成
    lg = logging.getLogger(__name__)
    lg.setLevel(logging.DEBUG)

    # ストリームハンドラ 追加
    sh = logging.StreamHandler()
    sh.setLevel(logging.DEBUG)
    sh.setFormatter(logging.Formatter('%(message)s'))
    lg.addHandler(sh)
    del sh

    # ファイルハンドラ 追加
    fh = logging.FileHandler(
        format_log, mode='a', encoding='utf-8',
        )
    fh.setLevel(logging.DEBUG)
    fh.setFormatter(logging.Formatter('%(message)s'))
    lg.addHandler(fh)
    del fh

    try:
        test_formats(lg)
    finally:
        # ロガー終了
        logging.shutdown() # 書かなくてもいいとのことでした(後述)
    return


def test_formats(lg):
    """フォーマットをすべて試す関数"""

    # LogRecord 属性の『フォーマット(fmt)』と『説明(ex)』
    log_formats = (
        ('%(asctime)s', '時刻 (人が読める形式)'),
        ('%(created)f', '時刻 (time.time()形式)'),
        ('%(filename)s', 'ファイル名'),
        ('%(funcName)s', '関数名'),
        ('%(levelname)s', 'ロギングレベルの名前'),
        ('%(levelno)s', 'ロギングレベルの数値'),
        ('%(lineno)d', '行番号'),
        # ('%(message)s', 'ログメッセージ'),
        ('%(module)s', 'モジュールの名前'),
        ('%(msecs)d', '時刻のミリ秒部分 (milliseconds)'),
        ('%(name)s', 'ロガーの名前'),
        ('%(pathname)s', 'ファイルパス'),
        ('%(process)d', 'プロセスID (PID)'),
        ('%(processName)s', 'プロセス名'),
        ('%(relativeCreated)d', 'logging モジュール読み込みからの時刻 (ミリ秒)'),
        ('%(thread)d', 'スレッドID'),
        ('%(threadName)s', 'スレッド名'),
        )

    # (フォーマット名が一番長い要素を取得)
    log_format_max = max(log_formats, key=lambda x: len(x[0]))

    # (フォーマット名の長さを取得)
    len_max = len(log_format_max[0])

    lg.debug('start《 LogRecord 属性の出力テスト 》')
    lg.debug(f'{"[ format ]".ljust(len_max)} -> [ 出力 ] -> [ 説明 ]')

    for (fmt, ex) in log_formats:

        # すべてのハンドラのフォーマッタを変更
        for h in lg.handlers:
            h.setFormatter(logging.Formatter(f'%(message)s -> {fmt} -> {ex}'))

        # ログ記録
        lg.debug(fmt.ljust(len_max))
    else:
        # すべてのハンドラのフォーマッタを変更
        for h in lg.handlers:
            h.setFormatter(logging.Formatter('%(message)s'))

    lg.debug('end')
    return


if __name__ == '__main__':
    main()

コードの説明と Python マニュアルの場所

コードの説明と Python 公式マニュアルの場所です。

pathlib パスリブの説明

pathlibパスリブ は、パス文字列を自在に操作できるモジュールです。

os.path の関数を2重、3重に書く必要がなくなったので、とても便利でした。

『パスのインスタンス』を作るための Path クラス
class pathlib.Path(*pathsegments)

パスを後ろに連結する .joinpathジョインパス() メソッド
PurePath.joinpath(*other)

『パスのインスタンス』から元のパス文字列を取得するときは、Python 組み込み関数の str() を使います。
p = pathlib.Path('パス文字列')
text = str(p)

logging モジュールのファイルハンドラ .FileHandler() などは、『パスのインスタンス』も受け取ってくれました。

なので、.FileHandler(str(p)) のように書く必要はなかったです。.FileHandler(p) だけで OK でした。

logging ロギングの説明

loggingロギング は、ログを出力するためのモジュールです。

ロガーを作る『ゲットロガー
モジュールレベルの関数
logging.getLogger(name=None)

ロギングレベルを設定する『セットレベル
『ロガー』オブジェクトの setLevel(level)
『ハンドラ』オブジェクトの setLevel(level)

ログの出力フォーマットを指定する『セットフォーマッター
ハンドラオブジェクト
setFormatter(fmt)
この関数は、fmt に logging.Formatter() を渡して使います。

setFormatter() に渡すフォーマッターを作る『フォーマッター
フォーマッタオブジェクト
class logging.Formatter(fmt=None, datefmt=None, style='%')

ロガーに『ストリームハンドラ』や『ファイルハンドラ』を追加する『アッドハンドラ
ロガーオブジェクト
addHandler(hdlr)

ログ出力で使えるフォーマットの一覧表。
LogRecord 属性(ログレコード属性)
setFormatter(logging.Formatter(fmt)) の fmt に『フォーマット』を渡して使います。

実際にログを出力するときに使うメソッド『デバッグ
モジュールレベルの関数
logging.debug(msg, *args, **kwargs)
ここで指定した文字列が、『'%(message)s'』の部分に入りました。
(msg % args として求められた文字列が入ります)

ロギングシステムを終了する『シャットダウン
logging.shutdown()

※ Python 3.8 のマニュアルから、『通常は手動で実行する必要は無い』という旨の説明が追加されていました。

When the logging module is imported, it registers this function as an exit handler (see atexit), so normally there’s no need to do that manually.

https://docs.python.org/ja/3/library/logging.html#logging.shutdown

要約すると、『import logging をしたときに、終了ハンドラとして logging.shutdown() が登録されるから、通常は手動で実行する必要はないです。』ということのようです。

文字列の整形に使用した関数・メソッドの説明

ログ出力で、文字列の『書式』や『幅』を調整するために使用した、関数とメソッドの説明です。

一番長い文字列を探すために使った『マックス
組み込み関数
max(iterable, *[, key, default])
引数ひきすうの key にラムダ式を渡すと、その式を使って『最大』の要素を返してくれます。
max() に『リストのリスト』や『タプルのタプル』を渡すと、『最大』を含んでいるところのリストやタプルを返してくれます

max() で最大を求めるために使った『ラムダ式
式 (expression)
ラムダ (lambda)
コード例の max(log_formats, key=lambda x: len(x[0])) では、log_formats の各タプルの中の [0] 番目の要素(フォーマットの要素)で、文字列の長さ len(x[0]) を見て、『最大』を求めています。

リストや文字列の長さを取得する『レン
組み込み関数
len(s)

文字列を左寄せして空白で埋める時は『エルジャスト
組み込み型 文字列メソッド
str.ljust(width[, fillchar])

文字列を右寄せして空白で埋める時は『アールジャスト
組み込み型 文字列メソッド
str.rjust(width[, fillchar])

文字列を中央に寄せて空白で埋める時は『センター
組み込み型 文字列メソッド
str.center(width[, fillchar])

関数やメソッドを使わずに、パーセント % でみぎ揃え、ひだり揃えにする方法もあります。

『9文字』の範囲で『右寄せ →』や『← 左寄せ』をする Python コード例です。

text = 'abc'
print('%s' % text)
print('%9s みぎ寄せ' % text)
print('%-9s ひだり寄せ' % text)

import logging
lg = logging.getLogger(__name__)
lg.setLevel(logging.DEBUG)
sh = logging.StreamHandler()
sh.setLevel(logging.DEBUG)
lg.addHandler(sh)
sh.setFormatter(logging.Formatter('%(message)s'))
lg.debug('abc')
sh.setFormatter(logging.Formatter('%(message)9s みぎ寄せ'))
lg.debug('abc')
sh.setFormatter(logging.Formatter('%(message)-9s ひだり寄せ'))
lg.debug('abc')

実行結果

abc
      abc みぎ寄せ
abc       ひだり寄せ
abc
      abc みぎ寄せ
abc       ひだり寄せ

アルファベットのエフ f から始まる文字列 f'' は『フォーマット済み文字列リテラル (f-string)
字句じく解析
フォーマット済み文字列リテラル
文字列の中に変数を自然に埋め込めたので、パーセント % や str.format() を使った方法よりも、コードが書きやすかったです。

タイトルとURLをコピーしました