| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108 |
- """阿里云 SLS 日志上报接入(2026-06-11)。
- 数据流:
- Python logger → 本地 FileHandler(全量保留)
- → QueuedLogHandler(aliyun-log-python-sdk 自带,异步队列 + 批量 flush)
- → SLS Logstore
- 设计原则:
- · 配置缺失 → SLS_ENABLED=False → 不上报,主链路不受影响(降级)
- · 上报失败 → SDK 内部 retry + 异常吞掉(不抛出),主链路不受影响
- · INFO+ 全量上报(用户 2026-06-11 决策,日量级约 5k-20k 条)
- """
- from __future__ import annotations
- import logging
- from typing import Optional
- logger = logging.getLogger(__name__)
- def attach_sls_handler(root_logger: Optional[logging.Logger] = None) -> bool:
- """给 root_logger 挂上 SLS QueuedLogHandler(若 SLS_ENABLED)。
- Returns:
- True 已挂上 / False 跳过(配置缺失 / SDK 缺失 / 异常)
- """
- if root_logger is None:
- root_logger = logging.getLogger()
- try:
- from config import (
- SLS_ENABLED,
- SLS_ENDPOINT,
- SLS_ACCESS_KEY_ID,
- SLS_ACCESS_KEY_SECRET,
- SLS_PROJECT,
- SLS_LOGSTORE,
- SLS_LOG_LEVEL,
- )
- except ImportError as e:
- logger.warning("[sls] config 缺 SLS_* 变量,跳过:%s", e)
- return False
- if not SLS_ENABLED:
- logger.info("[sls] SLS_ENABLED=False(凭证未完整配置),跳过上报")
- return False
- try:
- from aliyun.log import QueuedLogHandler
- except ImportError:
- logger.warning("[sls] aliyun-log-python-sdk 未安装,跳过上报(.venv/bin/pip3 install aliyun-log-python-sdk)")
- return False
- try:
- sls_handler = QueuedLogHandler(
- end_point=SLS_ENDPOINT,
- access_key_id=SLS_ACCESS_KEY_ID,
- access_key=SLS_ACCESS_KEY_SECRET,
- project=SLS_PROJECT,
- log_store=SLS_LOGSTORE,
- # 自动提取 record 标准字段
- fields=["levelname", "name", "filename", "lineno", "thread", "process"],
- # message 中 k=v 形式自动解析为独立字段(便于 SLS 索引/查询)
- extract_kv=True,
- )
- sls_handler.setLevel(getattr(logging, SLS_LOG_LEVEL, logging.INFO))
- sls_handler.setFormatter(logging.Formatter(
- "%(asctime)s | %(levelname)s | %(name)s | %(message)s",
- datefmt="%Y-%m-%d %H:%M:%S",
- ))
- root_logger.addHandler(sls_handler)
- logger.info(
- "[sls] 已挂载 QueuedLogHandler → %s/%s/%s (level=%s)",
- SLS_ENDPOINT, SLS_PROJECT, SLS_LOGSTORE, SLS_LOG_LEVEL,
- )
- return True
- except Exception as e:
- logger.warning("[sls] 挂载失败(降级为不上报,主链路不受影响):%s", e)
- return False
- def smoke_test() -> bool:
- """烟囱测试:发一条 INFO + 一条 WARNING + 一条 ERROR,等 3 秒让队列 flush。
- 用法:python -c "from tools.sls_setup import smoke_test; smoke_test()"
- 然后去 SLS 控制台 logstore=info-log 查看是否收到。
- """
- import time
- logging.basicConfig(
- level=logging.INFO,
- format="%(asctime)s | %(levelname)s | %(name)s | %(message)s",
- datefmt="%H:%M:%S",
- )
- ok = attach_sls_handler()
- if not ok:
- logger.error("[smoke_test] SLS 未挂上,无法测试")
- return False
- test_logger = logging.getLogger("sls_smoke_test")
- test_logger.info("[smoke_test] hello SLS - INFO 消息 event=smoke_info")
- test_logger.warning("[smoke_test] hello SLS - WARNING 消息 event=smoke_warn")
- test_logger.error("[smoke_test] hello SLS - ERROR 消息 event=smoke_error code=999")
- logger.info("[smoke_test] 已发 3 条,等 3s 让 QueuedLogHandler flush")
- time.sleep(3)
- logger.info("[smoke_test] 测试完成,请去 SLS 控制台 logstore 查看")
- return True
|