ルートロガーでログを記録する Python コード例

Python 標準ライブラリの loggingロギング を使用して、ログを記録するコード例です。

root loggerルートロガー を使用したコード例の紹介です。

『プログラムのスタート地点となる Python ファイル(トップレベルのコード)』や、『Python ファイル1つだけのコード』を書くときに、自分はよく、以下のように書いています。

ルートロガーを使用したコード例

root loggerルートロガー を使用した Python コード例です。

"""Python ファイル名 logging_main.py"""

from pathlib import Path
import logging


if __name__ == '__main__':
    logger = logging.getLogger()
    # ルートロガー (root logger) を取得
    # 自身をスクリプトとして実行したときなど
else:
    logger = logging.getLogger(__name__)
    # 名前付きのロガーを取得
    # __name__ 属性の例
    #
    # ● 他のPythonモジュールから呼び出したとき、または、
    # 他のPythonモジュールからマルチプロセス処理で呼び出したときは
    # 'logging_main' (自身のモジュール名) になった。
    #
    # ● 自身をマルチプロセス処理で実行したときは
    # '__mp_main__' になった。


# Python ファイル名を表示
print(f'Python ファイル名 -> {Path(__file__).name}')

# __name__ 属性を表示
print(f'__name__ 属性 -> {__name__}')


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

    # ロガーのロギングレベルを設定
    logger.setLevel(logging.DEBUG)

    # ストリームハンドラを取得 → ハンドラのロギングレベルを設定 → ロガーに追加
    sh = logging.StreamHandler()
    sh.setLevel(logging.DEBUG) # デバッグ レベル
    logger.addHandler(sh)

    # ログファイルのパスを決める
    log_txt = Path(__file__).parent.joinpath(f'log-{logger.name}.txt')
    # Path(__file__).parent -> Python ファイルと同じフォルダ
    # .joinpath(f'log_{logger.name}.txt') -> ロガー名を入れたファイル名

    # ファイルハンドラを取得 → ハンドラのロギングレベルを設定 → ロガーに追加
    fh = logging.FileHandler(log_txt, encoding='utf-8')
    fh.setLevel(logging.WARNING) # ワーニング/ウォーニング レベル
    # fh.setLevel(logging.CRITICAL) # クリティカル レベル
    logger.addHandler(fh)


    # ロガーを使う
    logger.debug(f'ロガー名 logger.name -> {logger.name}')
    logger.debug(f'ログファイル名 fh.baseFilename -> {fh.baseFilename}')
    logger.debug(f'{logger.name} 開始')
    logger.warning(f'{logger.name} ワーニング (例) パスが255文字を超えています')
    logger.error(f'{logger.name} エラー (例) ファイルが存在しません')
    logger.debug(f'{logger.name} 終了')


    # ログのファイルサイズがゼロなら削除する (必要なら)
    if log_txt.stat().st_size == 0:
        # ファイルハンドラを閉じる
        # (これでログファイルを削除できるようになった)
        fh.close()

        # 空っぽのログファイルを削除
        log_txt.unlink()
    return


if __name__ == '__main__':
    main()

実行結果

コマンドプロンプトの表示

『print() 関数』と『ロガーのストリームハンドラ』が表示した内容です。

Python ファイル名 -> logging_main.py
__name__ 属性 -> __main__
ロガー名 logger.name -> root
ログファイル名 fh.baseFilename -> ***\log-root.txt
root 開始
root ワーニング (例) パスが255文字を超えています
root エラー (例) ファイルが存在しません
root 終了

ログファイルの内容

『ロガーのファイルハンドラ』が記録した内容です。

root ワーニング (例) パスが255文字を超えています
root エラー (例) ファイルが存在しません

ファイルハンドラは、ロギングレベルの設定で fh.setLevel(logging.WARNING) を指定しました。

なので、WARNING よりも軽微なログである logger.debug() が無視されています。

logger の変数はグローバル変数に置きました

logging.getLogger() で取得した logger は、グローバルに置きました。

自分は、ロガーをどこのスコープに置くかで『グローバル変数に置くアプローチ』と『関数の中のローカル変数に置くアプローチ』の2通りを試してきました。

その結果、たいていのケースで『グローバル変数に置くアプローチ』のほうが良かったので、そのアプローチで書き始めることが多くなってきました。

最初は『変数のスコープを狭くできるから』とか『ロガーの有無で動作を変えたい』みたいな理由で、ローカル変数に置くアプローチをとっていました。

しかしながら、Python のロガーの使い方を知っていくうちに、『ローカル変数じゃなくてもできた』ということが少しずつ出てきました。

そういうわけで、だんだんと『ロガーをグローバルに置くアプローチ』をとるようになってきました。

ところで、ロギングのベストプラクティスに関しては、正直いまだに把握できていません。

『これだ!』と思っても、くつがえくつがえる。

ほかにも『ルートロガーを使うべきか?名前付きロガーを使うべきか?』とか、『logging.debug() を使うべきか?logger.debug() を使うべきか?』とか、長い間いろいろと迷いながら使っていました。

それらにくわえて、『マルチプロセス処理の時はどうするのか?』とか、『Ubuntu や Linux Mint と Windows の両方に対応するときはどうするのか?』とかも出てきました。

必要が生じるたびに、やり方を調べて、ログをとってきました。

そして、もはや全部のケースを見越したベストプラクティスの探索は無理だと感じてきました。

欲しかったログが取れれば、それが正解。

そう思うようになりました。

Python マニュアルの場所

コード例で使用した機能のマニュアルの場所です。

インポート関連のモジュール属性

ダブルアンダースコアーネーム __name__
__name__

ダブルアンダースコアーファイル __file__
__file__

スコープの名前

ダブルアンダースコアーメイン '__main__'
__main__ — トップレベルのスクリプト環境

f'' エフストリング (f-string)

エフ f'' から始まる文字列
フォーマット済み文字列リテラル

pathlib パスリブ

パス クラス Path()
class pathlib.Path(*pathsegments)

ネーム プロパティ .name
PurePath.name

ペアレント プロパティ .parent
PurePath.parent

ジョインパス メソッド .joinpath()
PurePath.joinpath(*other)

スタット メソッド .stat()
Path.stat()

エスティーサイズ .st_size
st_size

アンリンク メソッド .unlink()
Path.unlink(missing_ok=False)

logging ロギング

ゲットロガー メソッド .getLogger()
logging.getLogger(name=None)

セットレベル メソッド .setLevel()
『ロガー』オブジェクトの setLevel(level)
『ハンドラ』オブジェクトの setLevel(level)

ストリームハンドラ クラス logging.StreamHandler()
class logging.StreamHandler(stream=None)

ファイルハンドラ クラス logging.FileHandler()
class logging.FileHandler(filename, mode='a', encoding=None, delay=False)

アッドハンドラ メソッド .addHandler()
addHandler(hdlr)

デバッグ メソッド .debug()
debug(msg, *args, **kwargs)

ウォーニング メソッド .warning()
warning(msg, *args, **kwargs)

エラー メソッド .error()
error(msg, *args, **kwargs)

クローズ メソッド .close()
close()

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