1
0

logger.py 3.0 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. def _build_fluent_handler() -> Optional[logging.Handler]:
  10. """
  11. 创建 Fluentd Handler。
  12. 若 fluent-logger 未安装或初始化失败,返回 None。
  13. """
  14. if not settings.FLUENTD_ENABLED:
  15. return None
  16. if fluent_handler is None:
  17. logging.getLogger(__name__).warning("fluent-logger 未安装,已跳过 Fluentd 日志输出。")
  18. return None
  19. try:
  20. fh = fluent_handler.FluentHandler(
  21. tag=settings.FLUENTD_EFFECTIVE_TAG,
  22. host=settings.FLUENTD_HOST,
  23. port=settings.FLUENTD_PORT,
  24. timeout=settings.FLUENTD_TIMEOUT_SEC
  25. )
  26. # 输出结构化字段,避免 record 退化为 string。
  27. if hasattr(fluent_handler, "FluentRecordFormatter"):
  28. fh.setFormatter(fluent_handler.FluentRecordFormatter({
  29. "level": "%(levelname)s",
  30. "logger": "%(name)s",
  31. "message": "%(message)s",
  32. "time": "%(asctime)s",
  33. "app": settings.APP_NAME,
  34. "env": settings.APP_ENV,
  35. }))
  36. return fh
  37. except Exception as e:
  38. logging.getLogger(__name__).exception("初始化 Fluentd Handler 失败: %s", str(e))
  39. return None
  40. def setup_logging():
  41. """
  42. 配置日志系统,使其同时输出到文件和控制台。
  43. 这个函数应该在应用程序启动时只调用一次。
  44. """
  45. root_logger = logging.getLogger()
  46. # 避免重复初始化
  47. if root_logger.handlers:
  48. return
  49. root_logger.setLevel(getattr(logging, settings.LOG_LEVEL.upper(), logging.INFO))
  50. stream_formatter = logging.Formatter("%(asctime)s %(levelname)s [%(name)s] %(message)s")
  51. file_formatter = logging.Formatter("%(asctime)s %(levelname)s [%(name)s] %(message)s")
  52. console_handler = logging.StreamHandler(sys.stdout)
  53. console_handler.setFormatter(stream_formatter)
  54. root_logger.addHandler(console_handler)
  55. file_handler = logging.FileHandler('app.log', mode='w', encoding='utf-8')
  56. file_handler.setFormatter(file_formatter)
  57. root_logger.addHandler(file_handler)
  58. if not settings.FLUENTD_ENABLED:
  59. root_logger.info("日志系统已成功配置,将输出到控制台和 app.log 文件。")
  60. return
  61. fluentd_handler = _build_fluent_handler()
  62. if fluentd_handler:
  63. root_logger.addHandler(fluentd_handler)
  64. root_logger.info(
  65. "Fluentd日志输出已启用: host=%s port=%s tag=%s",
  66. settings.FLUENTD_HOST, settings.FLUENTD_PORT, settings.FLUENTD_EFFECTIVE_TAG
  67. )
  68. root_logger.info("日志系统已成功配置,将输出到控制台和 app.log 文件。")
  69. def get_logger(name: str) -> logging.Logger:
  70. """
  71. 获取一个指定名称的日志记录器实例。
  72. 假设 setup_logging() 已经在此之前被调用。
  73. """
  74. return logging.getLogger(name)