init medical training project
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
"""自定义日志 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
|
||||
Reference in New Issue
Block a user