1package logging
2
3import (
4 "fmt"
5 "os"
6 "os/signal"
7 "runtime"
8 "runtime/debug"
9 "syscall"
10)
11
12// SetupCrashHandler sets up signal handlers to catch crashes and log them
13func SetupCrashHandler() {
14 c := make(chan os.Signal, 1)
15
16 // Listen for crash signals
17 signal.Notify(c,
18 syscall.SIGSEGV, // Segmentation violation
19 syscall.SIGABRT, // Abort signal
20 syscall.SIGFPE, // Floating point exception
21 syscall.SIGBUS, // Bus error
22 syscall.SIGILL, // Illegal instruction
23 )
24
25 go func() {
26 sig := <-c
27 handleCrash(sig)
28 }()
29}
30
31// SetupPanicRecovery sets up a global panic recovery handler
32// This should be called with defer at the start of main functions
33func SetupPanicRecovery() {
34 if r := recover(); r != nil {
35 logPanic(r)
36 }
37}
38
39// handleCrash logs crash information and exits
40func handleCrash(sig os.Signal) {
41 logger := GetLogger()
42 if logger == nil {
43 fmt.Fprintf(os.Stderr, "CRASH: Caught signal %v but no logger available\n", sig)
44 os.Exit(1)
45 }
46
47 // Log the crash
48 crashMsg := fmt.Sprintf("CRASH: Caught fatal signal %v", sig)
49 logger.writeLog(FATAL, crashMsg, "CRASH")
50
51 // Log stack trace
52 stack := debug.Stack()
53 stackMsg := fmt.Sprintf("Stack trace:\n%s", string(stack))
54 logger.writeLog(FATAL, stackMsg, "CRASH")
55
56 // Log system info
57 sysInfo := fmt.Sprintf("Go version: %s, OS: %s, Arch: %s, NumCPU: %d",
58 runtime.Version(), runtime.GOOS, runtime.GOARCH, runtime.NumCPU())
59 logger.writeLog(FATAL, sysInfo, "CRASH")
60
61 // Log memory stats
62 var m runtime.MemStats
63 runtime.ReadMemStats(&m)
64 memInfo := fmt.Sprintf("Memory - Alloc: %d KB, TotalAlloc: %d KB, Sys: %d KB, NumGC: %d",
65 m.Alloc/1024, m.TotalAlloc/1024, m.Sys/1024, m.NumGC)
66 logger.writeLog(FATAL, memInfo, "CRASH")
67
68 // Force flush logs
69 if logger.rotator != nil {
70 logger.rotator.Close()
71 }
72
73 // Exit with error code
74 os.Exit(128 + int(sig.(syscall.Signal)))
75}
76
77// logPanic logs panic information
78func logPanic(r any) {
79 logger := GetLogger()
80 if logger == nil {
81 fmt.Fprintf(os.Stderr, "PANIC: %v but no logger available\n", r)
82 return
83 }
84
85 // Log the panic
86 panicMsg := fmt.Sprintf("PANIC: %v", r)
87 logger.writeLog(FATAL, panicMsg, "PANIC")
88
89 // Log stack trace
90 stack := debug.Stack()
91 stackMsg := fmt.Sprintf("Stack trace:\n%s", string(stack))
92 logger.writeLog(FATAL, stackMsg, "PANIC")
93
94 // Re-panic to maintain normal panic behavior
95 panic(r)
96}
97
98// LogCrashInfo logs information that might be helpful for crash analysis
99func LogCrashInfo(context string) {
100 logger := GetLogger()
101 if logger == nil {
102 return
103 }
104
105 logger.writeLog(DEBUG, fmt.Sprintf("Crash context checkpoint: %s", context), "CRASH_DEBUG")
106}