Python logging如何捕获格式化异常避免打断程序正常运行?
Python logging日志格式化异常不中断程序的优化方案
你当前遇到的异常本质是f-string的格式化/参数求值发生在log.info调用之前,还没进入日志逻辑就已经抛出异常,所以普通的日志方法封装拦不住,这也是你需要用上下文管理器包裹的核心原因。
以下是几个比上下文管理器更便捷的实现方案:
方案1:日志方法代理封装(推荐,改动最小)
先实现一个安全日志代理类,直接替换你原有的logger实例,日常调用和原生logging用法完全一致:
import logging from functools import wraps class SafeLogger: def __init__(self, logger): self._logger = logger def _safe_exec(self, log_func, *args, **kwargs): try: return log_func(*args, **kwargs) except Exception: # 吞掉所有日志相关异常,最多记录错误提示,不向外抛出 try: self._logger.exception("当前日志记录失败,已自动跳过") except: # 极端情况连异常日志都打不出时直接忽略 pass return None @wraps(logging.Logger.debug) def debug(self, *args, **kwargs): return self._safe_exec(self._logger.debug, *args, **kwargs) @wraps(logging.Logger.info) def info(self, *args, **kwargs): return self._safe_exec(self._logger.info, *args, **kwargs) @wraps(logging.Logger.warning) def warning(self, *args, **kwargs): return self._safe_exec(self._logger.warning, *args, **kwargs) @wraps(logging.Logger.error) def error(self, *args, **kwargs): return self._safe_exec(self._logger.error, *args, **kwargs) @wraps(logging.Logger.exception) def exception(self, *args, **kwargs): return self._safe_exec(self._logger.exception, *args, **kwargs)
使用方式:
# 原有logger初始化 log = logging.getLogger(__name__) # 仅需加一行替换为安全代理,后续所有日志调用写法完全不变 log = SafeLogger(log)
注意:如果使用f-string做日志格式化,字符串求值异常发生在日志方法调用前,上述代理类无法拦截。推荐配合logging自带的延迟格式化语法使用:
错误写法:log.info(f"message {di[key]}")(格式化在方法外执行,异常直接抛出)
正确写法:log.info("message %s", di.get(key, "key不存在"))(格式化在方法内执行,且用get避免字典缺key异常)
方案2:Lambda包装语法(适配f-string场景,无需修改代理逻辑)
如果要保留f-string的写法,同时要拦截所有参数求值、格式化异常,可以修改代理类的逻辑,支持传入可调用对象:
def _safe_exec(self, log_func, msg_or_callable, *args, **kwargs): try: msg = msg_or_callable() if callable(msg_or_callable) else msg_or_callable return log_func(msg, *args, **kwargs) except Exception: try: self._logger.exception("当前日志记录失败,已自动跳过") except: pass return None
调用时仅需把f-string用lambda包裹,比with语法简洁很多:
log.info(lambda: f"message {di[key]}")
即使di不存在对应key,也只会跳过这条日志,不会中断主程序运行。
方案3:场景化前置处理(轻量无侵入)
如果只是固定场景的异常(比如字典缺key、空对象属性取值),可以直接从根源避免异常,不需要修改日志框架:
- 字典取值用
di.get(key, 缺省值)代替di[key] - 对象属性取值用
getattr(obj, attr, 缺省值)代替obj.attr
内容的提问来源于stack exchange,提问作者yeger
相关产品推荐
相关产品推荐

