weekly_handler.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. import os
  2. import logging
  3. from datetime import datetime, timedelta
  4. from logging.handlers import RotatingFileHandler
  5. from foundation.infrastructure.tracing.trace_context import trace_filter
  6. def _get_week_range(dt: datetime) -> tuple:
  7. """获取 dt 所在周的周一和周日日期"""
  8. monday = dt - timedelta(days=dt.weekday())
  9. sunday = monday + timedelta(days=6)
  10. return monday, sunday
  11. def _get_week_dir_name(dt: datetime) -> str:
  12. """生成周目录名: 2026-W19_0505-0511"""
  13. monday, sunday = _get_week_range(dt)
  14. iso_cal = dt.isocalendar()
  15. week_num = iso_cal[1]
  16. return f"{monday.year}-W{week_num:02d}_{monday.strftime('%m%d')}-{sunday.strftime('%m%d')}"
  17. class WeeklyRotatingFileHandler(logging.Handler):
  18. """按周创建目录、按天创建日志文件的 Handler"""
  19. def __init__(self, base_dir: str, module_name: str, level_name: str,
  20. file_max_mb: int = 10, backup_count: int = 5):
  21. super().__init__()
  22. self.base_dir = base_dir
  23. self.module_name = module_name
  24. self.level_name = level_name
  25. self.file_max_bytes = file_max_mb * 1024 * 1024
  26. self.backup_count = backup_count
  27. self._current_week_dir = None
  28. self._current_date_str = None
  29. self._current_handler = None
  30. def _resolve_handler(self) -> RotatingFileHandler:
  31. now = datetime.now()
  32. week_dir = _get_week_dir_name(now)
  33. date_str = now.strftime('%m%d')
  34. if week_dir != self._current_week_dir or date_str != self._current_date_str:
  35. if self._current_handler:
  36. self._current_handler.close()
  37. self._current_week_dir = week_dir
  38. self._current_date_str = date_str
  39. dir_path = os.path.join(self.base_dir, week_dir)
  40. os.makedirs(dir_path, exist_ok=True)
  41. filename = os.path.join(
  42. dir_path,
  43. f"{date_str}_{self.module_name}_{self.level_name}.log"
  44. )
  45. self._current_handler = RotatingFileHandler(
  46. filename=filename,
  47. mode='a',
  48. maxBytes=self.file_max_bytes,
  49. backupCount=self.backup_count,
  50. encoding='utf-8',
  51. delay=True
  52. )
  53. self._current_handler.setFormatter(self.formatter)
  54. self._current_handler.addFilter(trace_filter)
  55. return self._current_handler
  56. def emit(self, record):
  57. try:
  58. handler = self._resolve_handler()
  59. if handler:
  60. handler.emit(record)
  61. except Exception:
  62. self.handleError(record)
  63. def create_weekly_file_handlers(module_name: str, base_dir: str,
  64. file_max_mb: int = 10, backup_count: int = 5) -> list:
  65. """为模块创建按周组织的日志 Handler 列表"""
  66. level_map = {
  67. 'debug': logging.DEBUG,
  68. 'info': logging.INFO,
  69. 'warning': logging.WARNING,
  70. 'error': logging.ERROR,
  71. 'critical': logging.CRITICAL,
  72. }
  73. handlers = []
  74. for level_name, level_value in level_map.items():
  75. h = WeeklyRotatingFileHandler(
  76. base_dir=base_dir,
  77. module_name=module_name,
  78. level_name=level_name,
  79. file_max_mb=file_max_mb,
  80. backup_count=backup_count,
  81. )
  82. h.setLevel(level_value)
  83. h.addFilter(lambda record, lvl=level_value: record.levelno >= lvl)
  84. handlers.append(h)
  85. return handlers