sls_setup.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. """阿里云 SLS 日志上报接入(2026-06-11)。
  2. 数据流:
  3. Python logger → 本地 FileHandler(全量保留)
  4. → QueuedLogHandler(aliyun-log-python-sdk 自带,异步队列 + 批量 flush)
  5. → SLS Logstore
  6. 设计原则:
  7. · 配置缺失 → SLS_ENABLED=False → 不上报,主链路不受影响(降级)
  8. · 上报失败 → SDK 内部 retry + 异常吞掉(不抛出),主链路不受影响
  9. · INFO+ 全量上报(用户 2026-06-11 决策,日量级约 5k-20k 条)
  10. """
  11. from __future__ import annotations
  12. import logging
  13. from typing import Optional
  14. logger = logging.getLogger(__name__)
  15. def attach_sls_handler(root_logger: Optional[logging.Logger] = None) -> bool:
  16. """给 root_logger 挂上 SLS QueuedLogHandler(若 SLS_ENABLED)。
  17. Returns:
  18. True 已挂上 / False 跳过(配置缺失 / SDK 缺失 / 异常)
  19. """
  20. if root_logger is None:
  21. root_logger = logging.getLogger()
  22. try:
  23. from config import (
  24. SLS_ENABLED,
  25. SLS_ENDPOINT,
  26. SLS_ACCESS_KEY_ID,
  27. SLS_ACCESS_KEY_SECRET,
  28. SLS_PROJECT,
  29. SLS_LOGSTORE,
  30. SLS_LOG_LEVEL,
  31. )
  32. except ImportError as e:
  33. logger.warning("[sls] config 缺 SLS_* 变量,跳过:%s", e)
  34. return False
  35. if not SLS_ENABLED:
  36. logger.info("[sls] SLS_ENABLED=False(凭证未完整配置),跳过上报")
  37. return False
  38. try:
  39. from aliyun.log import QueuedLogHandler
  40. except ImportError:
  41. logger.warning("[sls] aliyun-log-python-sdk 未安装,跳过上报(.venv/bin/pip3 install aliyun-log-python-sdk)")
  42. return False
  43. try:
  44. sls_handler = QueuedLogHandler(
  45. end_point=SLS_ENDPOINT,
  46. access_key_id=SLS_ACCESS_KEY_ID,
  47. access_key=SLS_ACCESS_KEY_SECRET,
  48. project=SLS_PROJECT,
  49. log_store=SLS_LOGSTORE,
  50. # 自动提取 record 标准字段
  51. fields=["levelname", "name", "filename", "lineno", "thread", "process"],
  52. # message 中 k=v 形式自动解析为独立字段(便于 SLS 索引/查询)
  53. extract_kv=True,
  54. )
  55. sls_handler.setLevel(getattr(logging, SLS_LOG_LEVEL, logging.INFO))
  56. sls_handler.setFormatter(logging.Formatter(
  57. "%(asctime)s | %(levelname)s | %(name)s | %(message)s",
  58. datefmt="%Y-%m-%d %H:%M:%S",
  59. ))
  60. root_logger.addHandler(sls_handler)
  61. logger.info(
  62. "[sls] 已挂载 QueuedLogHandler → %s/%s/%s (level=%s)",
  63. SLS_ENDPOINT, SLS_PROJECT, SLS_LOGSTORE, SLS_LOG_LEVEL,
  64. )
  65. return True
  66. except Exception as e:
  67. logger.warning("[sls] 挂载失败(降级为不上报,主链路不受影响):%s", e)
  68. return False
  69. def smoke_test() -> bool:
  70. """烟囱测试:发一条 INFO + 一条 WARNING + 一条 ERROR,等 3 秒让队列 flush。
  71. 用法:python -c "from tools.sls_setup import smoke_test; smoke_test()"
  72. 然后去 SLS 控制台 logstore=info-log 查看是否收到。
  73. """
  74. import time
  75. logging.basicConfig(
  76. level=logging.INFO,
  77. format="%(asctime)s | %(levelname)s | %(name)s | %(message)s",
  78. datefmt="%H:%M:%S",
  79. )
  80. ok = attach_sls_handler()
  81. if not ok:
  82. logger.error("[smoke_test] SLS 未挂上,无法测试")
  83. return False
  84. test_logger = logging.getLogger("sls_smoke_test")
  85. test_logger.info("[smoke_test] hello SLS - INFO 消息 event=smoke_info")
  86. test_logger.warning("[smoke_test] hello SLS - WARNING 消息 event=smoke_warn")
  87. test_logger.error("[smoke_test] hello SLS - ERROR 消息 event=smoke_error code=999")
  88. logger.info("[smoke_test] 已发 3 条,等 3s 让 QueuedLogHandler flush")
  89. time.sleep(3)
  90. logger.info("[smoke_test] 测试完成,请去 SLS 控制台 logstore 查看")
  91. return True