// Package crash 崩溃兜底:panic 时把堆栈写入崩溃日志(闪退可诊断)。 package crash import ( "fmt" "os" "path/filepath" "runtime/debug" "sync" "time" ) var ( logMu sync.Mutex logPath string ) // SetLogPath 设置崩溃日志路径(main 启动时调用一次)。 func SetLogPath(p string) { logPath = p } func writeLog(msg string) { logMu.Lock() defer logMu.Unlock() msg = fmt.Sprintf("[%s] %s\n", time.Now().Format("2006-01-02 15:04:05"), msg) if logPath != "" { _ = os.MkdirAll(filepath.Dir(logPath), 0o755) if f, err := os.OpenFile(logPath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o644); err == nil { _, _ = f.WriteString(msg) _ = f.Close() } } _, _ = fmt.Fprint(os.Stderr, msg) } // Guard 在 main 顶部 defer Guard();panic 时写崩溃日志后重抛(保留退出行为)。 func Guard(logPath string) { if r := recover(); r != nil { writeLog(fmt.Sprintf("崩溃(主线程): %v\n%s", r, debug.Stack())) panic(r) // 保留原始 panic 行为(退出码非 0) } } // RecoverGo 非主 goroutine 的兜底:panic 写日志后终止进程 // (Go 中任何 goroutine 未恢复的 panic 都会导致整个进程静默退出)。 func RecoverGo(name string) { if r := recover(); r != nil { writeLog(fmt.Sprintf("崩溃(goroutine %s): %v\n%s", name, r, debug.Stack())) os.Exit(2) } }