|
|
@@ -0,0 +1,102 @@
|
|
|
+import os
|
|
|
+import logging
|
|
|
+from datetime import datetime, timedelta
|
|
|
+from logging.handlers import RotatingFileHandler
|
|
|
+from foundation.infrastructure.tracing.trace_context import trace_filter
|
|
|
+
|
|
|
+
|
|
|
+def _get_week_range(dt: datetime) -> tuple:
|
|
|
+ """获取 dt 所在周的周一和周日日期"""
|
|
|
+ monday = dt - timedelta(days=dt.weekday())
|
|
|
+ sunday = monday + timedelta(days=6)
|
|
|
+ return monday, sunday
|
|
|
+
|
|
|
+
|
|
|
+def _get_week_dir_name(dt: datetime) -> str:
|
|
|
+ """生成周目录名: 2026-W19_0505-0511"""
|
|
|
+ monday, sunday = _get_week_range(dt)
|
|
|
+ iso_cal = dt.isocalendar()
|
|
|
+ week_num = iso_cal[1]
|
|
|
+ return f"{monday.year}-W{week_num:02d}_{monday.strftime('%m%d')}-{sunday.strftime('%m%d')}"
|
|
|
+
|
|
|
+
|
|
|
+class WeeklyRotatingFileHandler(logging.Handler):
|
|
|
+ """按周创建目录、按天创建日志文件的 Handler"""
|
|
|
+
|
|
|
+ def __init__(self, base_dir: str, module_name: str, level_name: str,
|
|
|
+ file_max_mb: int = 10, backup_count: int = 5):
|
|
|
+ super().__init__()
|
|
|
+ self.base_dir = base_dir
|
|
|
+ self.module_name = module_name
|
|
|
+ self.level_name = level_name
|
|
|
+ self.file_max_bytes = file_max_mb * 1024 * 1024
|
|
|
+ self.backup_count = backup_count
|
|
|
+
|
|
|
+ self._current_week_dir = None
|
|
|
+ self._current_date_str = None
|
|
|
+ self._current_handler = None
|
|
|
+
|
|
|
+ def _resolve_handler(self) -> RotatingFileHandler:
|
|
|
+ now = datetime.now()
|
|
|
+ week_dir = _get_week_dir_name(now)
|
|
|
+ date_str = now.strftime('%m%d')
|
|
|
+
|
|
|
+ if week_dir != self._current_week_dir or date_str != self._current_date_str:
|
|
|
+ if self._current_handler:
|
|
|
+ self._current_handler.close()
|
|
|
+ self._current_week_dir = week_dir
|
|
|
+ self._current_date_str = date_str
|
|
|
+
|
|
|
+ dir_path = os.path.join(self.base_dir, week_dir)
|
|
|
+ os.makedirs(dir_path, exist_ok=True)
|
|
|
+
|
|
|
+ filename = os.path.join(
|
|
|
+ dir_path,
|
|
|
+ f"{date_str}_{self.module_name}_{self.level_name}.log"
|
|
|
+ )
|
|
|
+ self._current_handler = RotatingFileHandler(
|
|
|
+ filename=filename,
|
|
|
+ mode='a',
|
|
|
+ maxBytes=self.file_max_bytes,
|
|
|
+ backupCount=self.backup_count,
|
|
|
+ encoding='utf-8',
|
|
|
+ delay=True
|
|
|
+ )
|
|
|
+ self._current_handler.setFormatter(self.formatter)
|
|
|
+ self._current_handler.addFilter(trace_filter)
|
|
|
+
|
|
|
+ return self._current_handler
|
|
|
+
|
|
|
+ def emit(self, record):
|
|
|
+ try:
|
|
|
+ handler = self._resolve_handler()
|
|
|
+ if handler:
|
|
|
+ handler.emit(record)
|
|
|
+ except Exception:
|
|
|
+ self.handleError(record)
|
|
|
+
|
|
|
+
|
|
|
+def create_weekly_file_handlers(module_name: str, base_dir: str,
|
|
|
+ file_max_mb: int = 10, backup_count: int = 5) -> list:
|
|
|
+ """为模块创建按周组织的日志 Handler 列表"""
|
|
|
+ level_map = {
|
|
|
+ 'debug': logging.DEBUG,
|
|
|
+ 'info': logging.INFO,
|
|
|
+ 'warning': logging.WARNING,
|
|
|
+ 'error': logging.ERROR,
|
|
|
+ 'critical': logging.CRITICAL,
|
|
|
+ }
|
|
|
+
|
|
|
+ handlers = []
|
|
|
+ for level_name, level_value in level_map.items():
|
|
|
+ h = WeeklyRotatingFileHandler(
|
|
|
+ base_dir=base_dir,
|
|
|
+ module_name=module_name,
|
|
|
+ level_name=level_name,
|
|
|
+ file_max_mb=file_max_mb,
|
|
|
+ backup_count=backup_count,
|
|
|
+ )
|
|
|
+ h.setLevel(level_value)
|
|
|
+ h.addFilter(lambda record, lvl=level_value: record.levelno >= lvl)
|
|
|
+ handlers.append(h)
|
|
|
+ return handlers
|