1
0

logger.py 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. import logging
  2. import sys
  3. from typing import Optional
  4. from app.core.config import settings
  5. try:
  6. from fluent import handler as fluent_handler
  7. except Exception: # pragma: no cover - 运行时兜底
  8. fluent_handler = None
  9. # 定义一个全局的日志格式
  10. LOG_FORMAT = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
  11. def _build_fluent_handler() -> Optional[logging.Handler]:
  12. """
  13. 创建 Fluentd Handler。
  14. 若 fluent-logger 未安装或初始化失败,返回 None。
  15. """
  16. if not settings.FLUENTD_ENABLED:
  17. return None
  18. if fluent_handler is None:
  19. logging.getLogger(__name__).warning("fluent-logger 未安装,已跳过 Fluentd 日志输出。")
  20. return None
  21. try:
  22. fh = fluent_handler.FluentHandler(
  23. tag=settings.FLUENTD_TAG,
  24. host=settings.FLUENTD_HOST,
  25. port=settings.FLUENTD_PORT,
  26. timeout=settings.FLUENTD_TIMEOUT_SEC
  27. )
  28. # 注意:FluentHandler 不应设置普通字符串 formatter,
  29. # 否则发送到 Fluentd 的 record 会退化为 string,导致 record_transformer 报错。
  30. return fh
  31. except Exception as e:
  32. logging.getLogger(__name__).error(f"初始化 Fluentd Handler 失败: {e}")
  33. return None
  34. def setup_logging():
  35. """
  36. 配置日志系统,使其同时输出到文件和控制台。
  37. 这个函数应该在应用程序启动时只调用一次。
  38. """
  39. # 获取根日志记录器
  40. root_logger = logging.getLogger()
  41. root_logger.setLevel(logging.INFO) # 设置根日志记录器的级别
  42. # 如果已经有处理器,先清空,防止重复添加
  43. if root_logger.hasHandlers():
  44. root_logger.handlers.clear()
  45. # 创建一个通用的格式化器
  46. formatter = logging.Formatter(LOG_FORMAT)
  47. # 1. 创建控制台处理器 (StreamHandler)
  48. # - 将日志输出到标准输出(您的终端)
  49. console_handler = logging.StreamHandler(sys.stdout)
  50. console_handler.setFormatter(formatter)
  51. root_logger.addHandler(console_handler)
  52. # 2. 创建文件处理器 (FileHandler)
  53. # - 将日志写入文件 app.log
  54. # - mode='w' 表示每次启动都覆盖旧日志
  55. # - encoding='utf-8' 确保正确处理中文字符
  56. file_handler = logging.FileHandler('app.log', mode='w', encoding='utf-8')
  57. file_handler.setFormatter(formatter)
  58. root_logger.addHandler(file_handler)
  59. # 3. 可选:创建 Fluentd 处理器 (fluent-logger)
  60. fluentd_handler = _build_fluent_handler()
  61. if fluentd_handler:
  62. root_logger.addHandler(fluentd_handler)
  63. logging.info(
  64. "Fluentd日志输出已启用: host=%s port=%s tag=%s",
  65. settings.FLUENTD_HOST, settings.FLUENTD_PORT, settings.FLUENTD_TAG
  66. )
  67. # 配置完成后,可以记录一条消息来确认
  68. logging.info("日志系统已成功配置,将同时输出到控制台和 app.log 文件。")
  69. def get_logger(name: str) -> logging.Logger:
  70. """
  71. 获取一个指定名称的日志记录器实例。
  72. 假设 setup_logging() 已经在此之前被调用。
  73. """
  74. return logging.getLogger(name)