如何捕获Python脚本被用户手动终止的事件并记录日志?
核心原因
关闭终端窗口不会触发KeyboardInterrupt对应的SIGINT信号,也不会走正常进程退出流程,因此try/except KeyboardInterrupt、atexit注册的回调都不会执行:
- Windows平台关闭控制台时,系统会向控制台绑定的所有进程发送
CTRL_CLOSE_EVENT,默认预留最多5秒清理时间后强制终止进程 - Linux/macOS平台关闭终端时,系统会向会话内所有进程发送
SIGHUP信号,默认行为是直接终止进程
实现方案
子进程侧信号捕获(优先方案)
直接在被启动的子进程代码中注册对应系统事件的处理回调,在回调内完成日志写入,参考实现如下:
import os import sys import signal import logging from datetime import datetime # 初始化日志配置 logging.basicConfig( filename='runtime.log', level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s' ) def exit_cleanup(signum=None, frame=None): # 写入终止日志 logging.info(f"进程被用户手动终止,触发事件码: {signum}, 终止时间: {datetime.now()}") # 强制刷新日志缓冲区,避免内容留在内存未写入磁盘 for handler in logging.root.handlers: handler.flush() # 直接退出,不要用sys.exit(),信号上下文下该方法无法正常执行 os._exit(0) if __name__ == "__main__": # 按平台注册对应终止事件 if sys.platform.startswith('win'): import ctypes from ctypes import wintypes kernel32 = ctypes.WinDLL('kernel32', use_last_error=True) HandlerRoutine = ctypes.WINFUNCTYPE(wintypes.BOOL, wintypes.DWORD) @HandlerRoutine def console_ctrl_handler(ctrl_type): # ctrl_type=2 对应CTRL_CLOSE_EVENT,即用户关闭控制台窗口 if ctrl_type == 2: exit_cleanup(signum=ctrl_type) return True return False kernel32.SetConsoleCtrlHandler(console_ctrl_handler, True) else: # 绑定Linux/macOS下终端关闭触发的SIGHUP、进程终止触发的SIGTERM signal.signal(signal.SIGHUP, exit_cleanup) signal.signal(signal.SIGTERM, exit_cleanup) # 原有业务逻辑从这里开始 logging.info("脚本正常启动") while True: # 业务运行逻辑 pass
父进程侧兜底监控
如果子进程因系统超时强杀没来得及执行清理逻辑,可在父进程层增加状态监控兜底,检测到子进程异常退出时记录日志,参考代码:
import sys import subprocess import logging from datetime import datetime logging.basicConfig( filename='runtime.log', level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s' ) if __name__ == "__main__": # 启动子进程,按平台设置对应参数新建独立终端/会话 CREATE_NEW_CONSOLE = 0x00000010 proc = subprocess.Popen( [sys.executable, "your_child_script.py"], creationflags=CREATE_NEW_CONSOLE if sys.platform.startswith('win') else 0, start_new_session=not sys.platform.startswith('win') ) proc.wait() exit_code = proc.returncode # 正常退出exit_code一般为0,被关闭窗口终止时,Windows下常见退出码为0xC000013A,Linux/macOS下SIGHUP触发的退出码为129 if exit_code != 0: logging.info(f"检测到子进程异常终止,退出码: {exit_code}, 终止时间: {datetime.now()}")
注意事项
- 信号/控制台事件的回调逻辑不要做耗时操作,Windows下清理超时后进程会被直接强制结束,可能导致日志写入失败
- 日志写入完成后必须手动刷新文件句柄缓冲区,默认IO操作带缓冲,不主动刷盘的话日志内容可能留在内存中丢失
- 可根据实际业务需要,在清理回调中增加资源释放、状态上报等逻辑,总耗时不要超过系统预留的清理时间窗口
内容的提问来源于stack exchange,提问作者pcp23
相关产品推荐
相关产品推荐

