Go程序被Task Manager/taskkill终止时如何写入停止日志?
在Go中处理Windows下任务管理器/taskkill终止时的日志写入问题
问题根源
在Windows环境中,不同终止方式触发的系统信号/事件存在差异:
- VS Code终端按
CTRL+C发送的是SIGINT信号,Go标准库os/signal可直接捕获。 taskkill命令默认(不加/f参数)发送SIGTERM信号,但若加/f则是强制终止(调用TerminateProcess),这种情况无法捕获。- 任务管理器的"结束任务"操作,针对控制台程序通常触发控制台关闭事件,而非标准POSIX信号,
os/signal包无法直接处理。
解决方案
1. 同时监听SIGINT和SIGTERM信号
覆盖CTRL+C和非强制taskkill场景:
package main import ( "log" "os" "os/signal" "syscall" ) func main() { logFile, err := os.OpenFile("app.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { log.Fatal(err) } defer logFile.Close() log.SetOutput(logFile) sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) <-sigChan log.Println("stopping") logFile.Sync() // 强制刷入磁盘,避免日志丢失 }
2. 处理Windows控制台关闭事件
针对任务管理器"结束任务"或关闭控制台窗口的场景,需调用Windows API捕获CTRL_CLOSE_EVENT事件,使用golang.org/x/sys/windows包实现:
先安装依赖:
go get golang.org/x/sys/windows
代码实现:
package main import ( "log" "os" "syscall" "golang.org/x/sys/windows" ) var ( kernel32 = windows.NewLazySystemDLL("kernel32.dll") setConsoleCtrlHandler = kernel32.NewProc("SetConsoleCtrlHandler") ) func setConsoleHandler(handler func(uint32) bool) error { h, err := syscall.NewCallback(handler) if err != nil { return err } _, _, err = setConsoleCtrlHandler.Call(h, 1) if err != syscall.Errno(0) { return err } return nil } func main() { logFile, err := os.OpenFile("app.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { log.Fatal(err) } defer logFile.Close() log.SetOutput(logFile) err = setConsoleHandler(func(dwCtrlType uint32) bool { switch dwCtrlType { case windows.CTRL_C_EVENT, windows.CTRL_CLOSE_EVENT, windows.CTRL_BREAK_EVENT, windows.CTRL_SHUTDOWN_EVENT: log.Println("stopping") logFile.Sync() return true // 标记事件已处理,系统不再执行默认终止逻辑 } return false }) if err != nil { log.Fatal(err) } select {} // 保持程序运行,模拟业务阻塞逻辑 }
3. 强制终止场景的局限性
若用户使用taskkill /f或任务管理器"结束进程"操作,系统会直接终止进程,此时任何代码都无法执行,无法写入日志。只能通过提示用户避免使用强制终止方式来规避。
关键注意点
- 日志写入后必须调用
File.Sync(),确保缓冲区内容刷入磁盘,防止进程终止导致日志丢失。 - 若为Windows GUI程序,还需额外处理
WM_CLOSE等窗口消息,控制台程序重点关注控制台关闭事件即可。
内容的提问来源于stack exchange,提问作者martinotecco
相关产品推荐
相关产品推荐

