Files
medical_training/config/logging_handlers.py
T

94 lines
2.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""自定义日志 Handler:按日期分文件,兼容 Windows 文件锁。
替代 TimedRotatingFileHandler,避免 Windows 上因 rename 操作遇到文件锁
PermissionError: [WinError 32])导致日志丢失的问题。
文件命名规则:{prefix}-YYYY-MM-DD.log
日期切换时自动打开新文件,无需 rename 旧文件。
"""
import logging
import time
from pathlib import Path
class DailyFileHandler(logging.Handler):
"""按日期自动分文件的日志 Handler。
与 TimedRotatingFileHandler 的关键区别:
- 文件直接以日期命名,日期切换时打开新文件,**不 rename 旧文件**
- 多进程(dev server + 测试 / 管理命令)可同时写入,互不阻塞
- 自动清理超过 backup_count 天的旧文件
dictConfig 用法::
'audit_file': {
'class': 'config.logging_handlers.DailyFileHandler',
'dir_path': '/path/to/logs',
'prefix': 'audit',
'backup_count': 30,
'formatter': 'verbose',
}
"""
def __init__(self, dir_path, prefix='audit', backup_count=30, encoding='utf-8'):
super().__init__()
self.dir_path = Path(dir_path)
self.dir_path.mkdir(parents=True, exist_ok=True)
self.prefix = prefix
self.backup_count = backup_count
self.encoding = encoding
self._current_date = None
self._stream = None
self._open_today()
def _today(self):
return time.strftime('%Y-%m-%d')
def _open_today(self):
"""打开当天的日志文件(追加模式)。"""
if self._stream:
try:
self._stream.close()
except OSError:
pass
self._current_date = self._today()
filepath = self.dir_path / f'{self.prefix}-{self._current_date}.log'
self._stream = open(filepath, 'a', encoding=self.encoding)
def emit(self, record):
try:
today = self._today()
if today != self._current_date:
self._open_today()
self._cleanup_old_files()
msg = self.format(record)
self._stream.write(msg + '\n')
self._stream.flush()
except Exception:
self.handleError(record)
def close(self):
self.acquire()
try:
if self._stream:
try:
self._stream.close()
except OSError:
pass
self._stream = None
finally:
self.release()
super().close()
def _cleanup_old_files(self):
"""删除超过 backup_count 天的旧日志文件。"""
if self.backup_count <= 0:
return
try:
files = sorted(self.dir_path.glob(f'{self.prefix}-*.log'))
for f in files[:-self.backup_count]:
f.unlink(missing_ok=True)
except OSError:
pass