やりたいこと
ログの出力先を年月日ごとに動的に変更する。
TimedRotatingFileHandlerを使用すれば似たようなことはできますが、今回はローテーションではなく出力先を動的に変更します。
LOG_DIR
├── 2025
│ ├── 01
│ │ ├── LOG_FILE.20250101.log
│ │ ├── ...
│ │ └── LOG_FILE.20250131.log
│ └── 02
│ ├── LOG_FILE.20250201.log
│ ├── ...
│ └── LOG_FILE.20250228.log <- このファイルを直接つくる
└── LOG_FILE <- このファイルはつくらない
TimedRotatingFileHandlerを使った実装は以下の記事を参照ください。
実装
FileHandlerから継承します。
書き込みのたびに、日時を取得してファイルを開きなおす必要があるので、TimedRotatingFileHandlerを利用するほうが賢明だとは思います。
サンプルコード
import time
from datetime import datetime
from logging import FileHandler, getLogger
from pathlib import Path
from zoneinfo import ZoneInfo
class MyFileHandler(FileHandler):
def __init__(
self,
filename,
mode="a",
encoding=None,
delay=False,
errors=None,
):
self.org_baseFilename = None # Noneで初期化する
super().__init__(filename, mode, encoding, delay, errors)
def _open(self):
# self.baseFilenameの初期値を覚えておく
if not self.org_baseFilename:
self.org_baseFilename = self.baseFilename
# filenameを取得する
self.baseFilename = self.namer(self.org_baseFilename)
# ディレクトリが存在しない場合は作成する
Path(self.baseFilename).parent.mkdir(parents=True, exist_ok=True)
return super()._open()
def namer(self, filename):
# 現在日時を取得する
now = datetime.now(ZoneInfo("Asia/Tokyo"))
year, month, day = now.strftime("%Y"), now.strftime("%m"), now.strftime("%d")
# filenameに現在日時を追加する
filepath = Path(filename)
filepath_with_date = (
filepath.parent / year / month / f"{filepath.name}_{year}{month}{day}"
)
# FIXME: **** **** ここから動作確認用コード(不要なら消す) **** ****
filepath_with_date = (
filepath_with_date.parent
/ f"{filepath_with_date.name}{now.strftime('%H%M%S')}"
)
# FIXME: **** **** ここまで動作確認用コード(不要なら消す) **** ****
return str(filepath_with_date)
def emit(self, record):
self.close() # 毎回開き直す
return super().emit(record)
# ログを出力してみる
logger = getLogger("logger")
logger.addHandler(MyFileHandler("LOG_FILE_NAME"))
for i in range(20):
logger.error(f"ERROR MESSAGE{i}")
time.sleep(0.2)