某段数据长期 100% 缺失,日志里清一色地写着"数据超时"。于是你顺着"网络不稳"的方向使劲:加重试、加延迟、换时段、换线路——全都不管用。真凶其实跟网络毫无关系,只是被日志误导了太久。
现象
一个抓取任务,某类数据长期拿不到,日志里稳定地报同一句话:
[ERROR] <数据源A> 数据超时,跳过
[ERROR] <数据源A> 数据超时,跳过
[ERROR] <数据源A> 数据超时,跳过
加了三层重试、把超时阈值调到很大、换了更稳定的线路,这段数据依然 100% 缺失。既然日志说"超时",那就该是网络问题——可网络明明排除了。
根因
问题叠了两层,一层在导入,一层在报错:
第一层:环境里根本没装那个依赖库。 抓数据要用到某个第三方库,但这台机器上它压根没安装。而代码里的 import 写在了 try 块之外:
import some_datasource_lib # ← 在 try 之外
def fetch():
try:
return some_datasource_lib.get(...)
except Exception as e:
log.error("数据超时 %r", e)
return None
因为 import 不在 try 里,ImportError(准确地说是 ModuleNotFoundError)会直接穿透,在模块加载阶段就抛出,根本进不了下面那个 except。
第二层:入口把所有异常一律贴上"超时"标签。 上层的入口函数用一个兜底的 except Exception 捕获了一切,然后不加区分地打印"数据超时":
try:
run_all_sources()
except Exception:
log.error("数据超时") # 无论真实原因是什么,都说“超时”
于是 ModuleNotFoundError: No module named 'some_datasource_lib' 这样一条再明确不过的线索,被硬生生改写成了"超时"。你拿着"超时"去查网络,方向从一开始就错了。
解决
第一步,把 import 放进 try,并在 ImportError 时给出明确提示。 让"缺依赖"这种问题在第一时间自我暴露:
try:
import some_datasource_lib
except ImportError:
log.error("未安装依赖 some_datasource_lib,请先执行 pip install some-datasource-lib")
some_datasource_lib = None
第二步,第一个失败不要连累其余。 给每个数据源各包一层独立的异常处理,而不是让所有源共享一个 try。否则第一个源一失败,后面的源全被跳过,表现成"一片都缺":
for name, source in sources.items():
try:
data = source.fetch()
save(name, data)
except Exception as e:
log.error("数据源 %s 抓取失败:%s: %s", name, type(e).__name__, e)
# 继续处理下一个源,不中断整体
第三步,错误文案按异常类型区分。 别再"一句话盖所有":
except ModuleNotFoundError as e:
log.error("缺少依赖:%s", e)
except TimeoutError as e:
log.error("网络超时:%s", e)
except Exception as e:
log.error("未知错误 %s:%s", type(e).__name__, e)
这样日志里一眼就能看出"是缺依赖,还是真超时"。
延伸与预防
一句总结:把不同异常统一成一句话,等于亲手抹掉线索。
日志的价值就在于区分"发生了什么"。如果无论哪种失败都写成同一个词,那日志除了告诉你"又失败了"之外毫无用处,反而会把人引向错误的排查方向。
几条实践建议:
try的范围要包住真正可能出错的东西。 尤其是import——它是最典型的"装没装"检查点,别把它放在try外面白白丢掉线索。- 兜底的
except Exception要谨慎。 它适合做"最后一道防线",但绝不能顺手篡改错误信息。兜底时可以记录type(e).__name__和原始 message,保住真相。 - 每个独立单元各管各的异常。 一个源、一个文件、一个任务的失败,不该让整批停止;隔离错误,结果才完整,问题也定位得更准。
- 错误信息带上"最小可复现信息":哪个源、什么类型的异常、原始消息。当你不确定要不要加日志时,问自己一句——"三个月后看到这条日志,我能直接开始修吗?"能,就够好了。
排查建议也很直接:当一类问题"怎么修都不好"时,回头怀疑一下你的日志是不是在骗你。 把异常原本的样子打出来,往往比自己苦思冥想快得多。