如何无侵入统计导入指定模块的所有脚本的执行时间
可行方案说明
完全可以在自研公共库层面实现全量逻辑,不需要修改任何一个现有业务脚本,核心是利用Python包导入初始化机制、栈帧检查、退出钩子三个特性实现,对业务代码零侵入。
具体实现逻辑
所有代码都写在自研公共库的__init__.py文件中——所有通过import *导入公共库的脚本,启动时都会自动执行该文件中的代码,不需要业务侧做任何改动。
- 启动时间埋点:在
__init__.py最顶部(所有其他业务逻辑导入之前)记录高精度启动时间,用time.perf_counter()保证耗时统计精度。 - 调用方校验:通过Python标准库
sys获取上层调用栈帧,也就是当前执行业务脚本的全局命名空间,做两个规则校验:- 检查指定的目标模块是否被导入到脚本全局空间
- 检查全局空间中同名对象的来源是否为公共库内置模块,排除脚本内部自定义同名变量/模块的情况
- 退出钩子注册:对校验通过的脚本,用标准库
atexit注册退出回调函数,脚本正常执行结束、异常终止时都会自动触发回调,计算并打印总耗时,脚本路径可以直接从栈帧对象中获取,不需要额外传参。
可直接复用的代码示例
把下面的代码放到公共库__init__.py的最顶部,修改配置项后即可生效,后面接公共库原有的导入、导出逻辑即可:
import sys import time import atexit # -------------------------- 配置项,按需修改 -------------------------- # 需要统计的公共库下的目标模块全路径 TARGET_MODULES = { "your_public_lib.utils", "your_public_lib.db_operator", } # 最小打印阈值,单位秒,低于该耗时的脚本不打印,填0则全量打印 MIN_COST_TO_PRINT = 0 # --------------------------------------------------------------------- # 记录脚本启动时间,必须放在所有逻辑最前面 _script_start_ts = time.perf_counter() # 获取业务脚本的栈帧、全局命名空间、脚本路径 _caller_frame = sys._getframe(1) _caller_global_ns = _caller_frame.f_globals _caller_script_path = _caller_frame.f_code.co_filename # 校验是否符合统计规则 _need_stat = False for _target_full_name in TARGET_MODULES: _target_short_name = _target_full_name.split(".")[-1] # 短名不存在于脚本全局空间,说明没导入该模块 if _target_short_name not in _caller_global_ns: continue _exist_obj = _caller_global_ns[_target_short_name] # 校验对象来源,排除脚本自定义同名内容的情况 if getattr(_exist_obj, "__name__", "") == _target_full_name: _need_stat = True break # 符合规则才注册退出打印逻辑 if _need_stat: def _print_exec_cost(): total_cost = time.perf_counter() - _script_start_ts if total_cost >= MIN_COST_TO_PRINT: print(f"\n[公共库耗时统计] 脚本路径: {_caller_script_path}") print(f"[公共库耗时统计] 总执行时长: {total_cost:.3f}s") atexit.register(_print_exec_cost) # 后面继续写公共库原有的代码即可,比如原有模块导入、__all__定义等
注意事项
- 所有统计逻辑用到的内部变量全部加下划线前缀,Python的
import *默认不会导出下划线开头的名称,不会污染业务脚本的命名空间。 - 整套逻辑只有栈帧检查、时间记录的微秒级开销,对业务脚本性能几乎无影响。
atexit注册的回调在脚本正常执行完成、抛出未捕获异常终止时都会触发,只有调用os._exit()强制杀进程的极端场景不会触发,常规业务场景完全覆盖。- 如果后续要新增统计的目标模块,只需要修改配置项里的
TARGET_MODULES集合即可,不需要改动其他逻辑。
内容的提问来源于stack exchange,提问作者hanz hanzsolo
相关产品推荐
相关产品推荐

