日志级别本身不触发告警,必须通过自定义handler重写emit()方法,在其中判断levelno≥error后调用告警api;filter仅路由日志,不能执行告警动作;需确保logger.level≤目标告警级别。

Python日志中如何让 ERROR 和 CRITICAL 自动触发告警
直接结论:日志级别本身不触发告警,必须通过 Handler 主动拦截并外发。默认的 StreamHandler 或 FileHandler 只负责输出,不带通知能力。
常见错误是以为调用 logger.error("msg") 就等于“发告警”,结果线上出问题却没人收到——因为没配任何外发逻辑。
- 真正起作用的是自定义
Handler子类,重写emit()方法,在里面判断record.levelno >= logging.ERROR后调用钉钉/邮件/企业微信 API - 不要在
filter()里做告警动作:它只过滤,不执行;且可能被多个 handler 复用,导致重复发送 - 务必在
emit()开头加try/except包裹外发逻辑,否则网络失败会静默吞掉整条日志
用 logging.Filter 做分级告警路由是否可行
可以做路由,但不能替代告警动作本身。Filter 的作用只是“让某条日志是否进入某个 Handler”,不是“让它发出去”。
典型误用场景:给邮件 Handler 配一个只放行 CRITICAL 的 Filter,却忘了这个 Handler 本身没实现发邮件逻辑,结果日志照常进文件,但邮件一封没发。
快速生成专业的 Python 脚本和应用代码。一键创建完整项目结构,支持CLI、API、爬虫、Bot、Django等多种项目类型,包含完整的项目结构、配置文件、依赖管理、测试、README和文档。
- Filter 适合配合多个 Handler 使用,例如:
SlackHandler绑levelno >= logging.WARNING,PagerDutyHandler绑levelno >= logging.CRITICAL - Filter 里的
filter(record)返回True表示“放行”,返回False表示“丢弃”,不抛异常、不阻塞、不发消息 - 注意 Filter 是链式执行的:多个 Filter 会依次调用,任一返回
False就终止传递
为什么 logging.getLogger().addHandler() 后告警仍不生效
最常见原因是 logger 的 level 设置过高,导致低级别记录根本进不了 Handler 链。
比如你为 ERROR 级别写了告警 Handler,但 logger 本身设了 logger.setLevel(logging.WARNING),那么 WARNING 日志能进 Handler,但不会触发你的告警逻辑(因为你只在 emit() 里判了 ERROR 以上);而 ERROR 日志虽然满足告警条件,却可能被上层 logger 的 propagate 机制漏掉。
- 检查 logger 实例的
.level值,确保 ≤ 你希望告警的最低级别(如要告警ERROR,则logger.level ) - 关闭不必要的
propagate = True,避免日志被根 logger 重复处理或被更高层 level 拦截 - 用
logger.debug("test")+logging.basicConfig(level=logging.DEBUG)快速验证日志是否真的抵达你的 Handler
告警消息体里怎么拿到异常堆栈和上下文变量
LogRecord 对象自带 record.exc_info 和 record.stack_info,但它们是元组或字符串,需手动格式化。直接拼接容易丢 traceback 或引发编码错误。
- 用
logging.Formatter.formatException(record.exc_info)安全提取堆栈字符串(自动处理 None 和格式化) - 想带局部变量?标准 logging 不支持。得靠
extra参数传入:logger.error("db timeout", extra={"sql": sql, "params": params}),然后在 Handler 的emit()中读record.sql - 注意
extra字典键不能和LogRecord原生属性名冲突(如msg、levelname),否则会被覆盖且无警告
真正难的不是写个发消息的 Handler,而是确保它在高并发、网络抖动、进程退出等边界下不丢告警也不卡主线程——异步发、带重试、加超时、单独线程池,这些才是线上稳定运行的关键点。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










