
本文介绍通过自定义 Formatter 清除特定日志处理器中的异常栈跟踪信息,实现同一 logger.exception() 调用向两个 RotatingFileHandler(一个含栈跟踪、一个不含)分别输出的精准日志分流方案。
本文介绍通过自定义 formatter 清除特定日志处理器中的异常栈跟踪信息,实现同一 `logger.exception()` 调用向两个 rotatingfilehandler(一个含栈跟踪、一个不含)分别输出的精准日志分流方案。
在 Python 标准日志系统中,logger.exception(msg) 本质是调用 logger.error(msg, exc_info=True),它会自动将当前异常的 exc_info、格式化后的 exc_text 和可选的 stack_info 注入日志记录(LogRecord)对象。这些字段正是最终输出栈跟踪的关键——Formatter 在 format() 方法中检测到 record.exc_info 非空时,便会拼接并渲染完整的 traceback。
因此,要实现“同一异常调用、双路径输出、仅一路含栈跟踪”,最直接且可靠的方式不是修改 Handler 行为或使用 Filter(Filter 无法修改 record 的 exc_info 等只读属性),而是为无栈跟踪的 Handler 指定一个定制 Formatter,在 format() 中主动清空相关字段:
import logging
from logging.handlers import RotatingFileHandler
class NoStackTraceFormatter(logging.Formatter):
def format(self, record):
# 彻底移除栈跟踪所需的所有上下文
record.exc_info = None
record.exc_text = None
record.stack_info = None
return super().format(record)
# 创建 logger 及两个 RotatingFileHandler
logger = logging.getLogger(__name__)
logger.setLevel(logging.ERROR)
# 含栈跟踪的日志文件(默认 Formatter)
handler_with_trace = RotatingFileHandler(
"error_with_trace.log",
maxBytes=10_000_000,
backupCount=5
)
handler_with_trace.setLevel(logging.ERROR)
# 无栈跟踪的日志文件(自定义 Formatter)
handler_no_trace = RotatingFileHandler(
"error_no_trace.log",
maxBytes=10_000_000,
backupCount=5
)
handler_no_trace.setLevel(logging.ERROR)
handler_no_trace.setFormatter(NoStackTraceFormatter())
logger.addHandler(handler_with_trace)
logger.addHandler(handler_no_trace)使用时,只需正常调用 logger.exception():
try:
result = 10 / 0
except ZeroDivisionError:
logger.exception("计算发生零除错误") # 同时写入两个文件,内容不同✅ 效果验证:
创建 Flask + Vue.js 全栈项目,采用单端口架构、uv 依赖管理、结构化日志和生产级前端设计。当用户要求以下操作时触发:创建新的 Flask+Vue 项目、搭建 Web 应用、构建全栈 Python+Vue 应用、初始化 Flask Web 项目、设置单端口 Flask 前后端项目。触发词:新建项目、创建项目、搭建项目、Flask项目、Vue项目。
立即学习“Python免费学习笔记(深入)”;
-
error_with_trace.log中将包含完整消息 + traceback; -
error_no_trace.log中仅有"计算发生零除错误"一行纯文本,无任何堆栈干扰,便于人工快速扫描或简单脚本解析。
⚠️ 注意事项:
-
不要尝试在 Filter 中修改
record.exc_info:Filter.filter()方法接收的是只读LogRecord副本,修改其属性对后续 Formatter 无效; -
NoStackTraceFormatter必须继承自logging.Formatter并显式调用super().format(),否则将丢失基础格式(如时间、级别、模块名等); - 若需保留部分结构化信息(如异常类型和简短消息),可改写
format()中仅清空record.exc_info,再手动构造精简异常字符串,但本文方案以“彻底剥离”为目标,简洁可靠; - 此方法完全兼容
RotatingFileHandler、TimedRotatingFileHandler等所有标准 Handler,无需额外封装或 monkey patch。
该方案轻量、标准、可维护,是解决多级日志可读性与调试性平衡问题的专业实践。

















