logger.py 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """
  4. 日志管理模块
  5. """
  6. import logging
  7. import sys
  8. from pathlib import Path
  9. from typing import Optional
  10. from config import config
  11. class LoggerManager:
  12. """日志管理器"""
  13. _loggers = {}
  14. @classmethod
  15. def get_logger(cls, name: str, log_file: Optional[str] = None) -> logging.Logger:
  16. """获取日志记录器"""
  17. if name in cls._loggers:
  18. return cls._loggers[name]
  19. logger = logging.getLogger(name)
  20. logger.setLevel(getattr(logging, config.log_level.upper()))
  21. # 避免重复添加处理器
  22. if logger.handlers:
  23. return logger
  24. # 控制台处理器
  25. console_handler = logging.StreamHandler(sys.stdout)
  26. console_handler.setLevel(getattr(logging, config.log_level.upper()))
  27. console_formatter = logging.Formatter(config.log_format)
  28. console_handler.setFormatter(console_formatter)
  29. logger.addHandler(console_handler)
  30. # 文件处理器
  31. if log_file:
  32. log_path = Path(config.data_dir) / log_file
  33. file_handler = logging.FileHandler(log_path, encoding='utf-8')
  34. file_handler.setLevel(getattr(logging, config.log_level.upper()))
  35. file_formatter = logging.Formatter(config.log_format)
  36. file_handler.setFormatter(file_formatter)
  37. logger.addHandler(file_handler)
  38. # WebSocket 实时日志处理器
  39. logger.addHandler(WebSocketLogHandler())
  40. cls._loggers[name] = logger
  41. return logger
  42. @classmethod
  43. def setup_root_logger(cls):
  44. """设置根日志记录器"""
  45. logging.basicConfig(
  46. level=getattr(logging, config.log_level.upper()),
  47. format=config.log_format,
  48. handlers=[
  49. logging.StreamHandler(sys.stdout),
  50. logging.FileHandler(Path(config.data_dir) / "app.log", encoding='utf-8'),
  51. WebSocketLogHandler(),
  52. ]
  53. )
  54. # 便捷函数
  55. def get_logger(name: str, log_file: Optional[str] = None) -> logging.Logger:
  56. """获取日志记录器的便捷函数"""
  57. return LoggerManager.get_logger(name, log_file)
  58. class WebSocketLogHandler(logging.Handler):
  59. """将日志通过实时日志器广播到 WebSocket 客户端"""
  60. def emit(self, record: logging.LogRecord) -> None:
  61. try:
  62. message = self.format(record)
  63. level = record.levelname
  64. source = record.name
  65. # 走同步接口,内部会尝试调度到事件循环
  66. # 延迟导入,避免循环依赖
  67. from realtime_logger import realtime_logger
  68. realtime_logger.broadcast_log_sync(message, level, source)
  69. except Exception:
  70. # 保证日志不因 WebSocket 发送失败而中断
  71. pass