You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何捕获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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 15:06:21