Python の RotatingFileHandler で、ログをローテーションして出力するコード例です。
『ログ出力』と『ログのファイルサイズによるローテーション』は、RotatingFileHandler() を使うとできました。
ログファイルを自動で分割しながら、テキストファイルに出力することができました。
古いログも、自動でリネームして、ログファイルをローテーションして、残してくれました。
コマンドプロンプトには何も表示されず、『ファイル』だけに出力できました。
ログのファイルサイズ maxBytes を指定したら、だいたいそのサイズで、自動的に分割してくれました。
ログのファイルサイズは、文字コードによって変わりました。
ログのバックアップの数 backupCount を指定したら、その数だけ、分割後の古いログを残してくれました。
全体のログサイズは『ログサイズ×(最新ログ1個+バックアップN個)』くらいに収まりました。
古いログが『流れて消えていく仕組み』を作ることができました。
ログサイズの設定をデフォルトの maxBytes=0 にしたら、ログが分割されず、どこまでも大きくなっていきました。
ちょうど、普通の FileHandler と同じような動作になりました。
とりあえずは RotatingFileHandler を maxBytes=0 で使っておいて、必要に応じてサイズを指定する、という使い方も便利でした。
使用したライブラリ
Python 公式マニュアルの場所です。
logging モジュール
- logging — Python 用ロギング機能
- ロガー class logging.Logger
- セットレベル setLevel(level)
- ロガーを取得
- ゲットロガー logging.getLogger(name=None)
- ロギングレベル の一覧表
- ハンドラのレベル設定
- セットレベル setLevel(level)
- ロガーにハンドラを追加
- アッドハンドラ addHandler(hdlr)
- ログ出力
- デバッグ debug(msg, *args, **kwargs)
- インフォ info(msg, *args, **kwargs)
- ウォーニング(ワーニング) warning(msg, *args, **kwargs)
- エラー error(msg, *args, **kwargs)
- クリティカル critical(msg, *args, **kwargs)
logging.handlers パッケージ
- logging.handlers — ロギングハンドラ
- ローテーティングファイルハンドラの説明 RotatingFileHandler
- ローテーティングファイルハンドラ class logging.handlers.RotatingFileHandler(filename, mode=’a’, maxBytes=0, backupCount=0, encoding=None, delay=False)
組み込み機能
- メイン
'__main__'
- ネーム属性 __name__
- インポート関連のモジュール属性
- ネーム属性 __name__
- raw strings
r''
の説明(引用符の前に『アール r 』を付けた文字列) - f-string
f''
の説明(引用符の前に『エフ f 』を付けた文字列、フォーマット済み文字列リテラル)
コード例
ロガーで、ログファイルをローテーションしながら記録する Python コード例です。
ローテーティングファイルハンドラ RotatingFileHandler() を使います。
※(2020年6月18日 追記)logging.getLogger() で取得したロガー lg は、『グローバル』で持つほうが、便利で適切だと思います。コード例では関数の中に入れたままですが、必ずしもそうする必要は無いと思ったため、補足として追記します。
"""RotatingFileHandlerでログ出力するPythonコード例"""
import logging
import logging.handlers
def main():
"""メイン関数"""
# ロガーを取得
lg = logging.getLogger(__name__)
lg.setLevel(logging.DEBUG)
# ローテーティングファイルハンドラを作成 & ロガーに追加
rh = logging.handlers.RotatingFileHandler(
r'F:\apps\data\rh.log', encoding='utf-8',
maxBytes=50,
backupCount=3,
)
rh.setLevel(logging.DEBUG)
lg.addHandler(rh)
lg.debug('start')
# (コード例) 架空の気温でログレベルを分けてみる
temperatures = [20, 25, 30, 35, 40, 45]
for t in temperatures:
if t < 24:
lg.debug(f'debug 気温 {t} ℃')
elif 24 <= t < 28:
lg.info(f'info 暑さに注意 {t} ℃')
elif 28 <= t < 31:
lg.warning(f'warning 暑さに警戒 {t} ℃')
elif 31 <= t < 36:
lg.error(f'error 暑さに厳重警戒 {t} ℃')
elif 36 <= t:
lg.critical(f'critical 危険な気温です {t} ℃')
lg.debug('end')
return
if __name__ == '__main__':
main()
ロガーとハンドラには、深刻度の低い logging.DEBUG をセットしました。
なので、どのログレベルの出力も記録されるはず。
あと、文字コードは encoding=’utf-8′ で、maxBytes=50 バイト (bytes) をセットしました。
ところで、1文字は何バイトか?
Windows のファイルのプロパティや、テキストエディタの機能で、テキストファイルをチェックしました。
utf-8 では、アルファベットなら1文字1バイト、日本語なら1文字3バイトの計算になりました。
日本語のバイト数は、大抵3バイトでしたが、『utf-8 4バイト 日本語』で検索したところ、例外もあるようでした。
さて、日本語としては、 maxBytes=50 バイトだと、 50 / 3 = 約 16 文字が入る計算です。
とりあえず、ログファイルが分割されて、それらの1つ1つが 50 バイトくらいに収まれば成功です。
実行結果
ログファイルの内容です。
ログの並びは、ローテーションの関係で、Python コードの並びと逆順になりました。
OS は Windows 7 64bit で、出力されたログファイルの改行コードは CR+LF でした。
Web 表示の都合で改行表記を省略しましたが、各行の末尾には、CR+LF の2バイトがくっついていました。
rh は RotatingFileHandler の略です。
rh.log(ファイルサイズ 44 バイト)
critical 危険な気温です 45 ℃
end
rh.log.1(ファイルサイズ 39 バイト)
critical 危険な気温です 40 ℃
rh.log.2(ファイルサイズ 36 バイト)
error 暑さに厳重警戒 35 ℃
rh.log.3(ファイルサイズ 32 バイト)
warning 暑さに警戒 30 ℃
どのログレベルも、ファイルに記録されていました。
あと、それぞれのファイルサイズも、50 バイト以下に収まっていました。
成功です。
maxBytes=0 の実行結果
ログサイズの設定を、デフォルトのゼロ maxBytes=0 にした時の結果です。
Python コード
rh = logging.handlers.RotatingFileHandler(
r'F:\apps\data\rh.log', encoding='utf-8',
maxBytes=0,
backupCount=3,
)
実行結果
rh.log(ファイルサイズ 208 バイト)
start
debug 気温 20 ℃
info 暑さに注意 25 ℃
warning 暑さに警戒 30 ℃
error 暑さに厳重警戒 35 ℃
critical 危険な気温です 40 ℃
critical 危険な気温です 45 ℃
end
ログファイルは、分割されなかったです。
古いログが流れて、消えることもなかったです。
1つのファイルに、すべてのログを残すことができました。