logger.go

 1package logging
 2
 3import (
 4	"fmt"
 5	"log/slog"
 6	"os"
 7	"runtime/debug"
 8	"time"
 9)
10
11func Info(msg string, args ...any) {
12	slog.Info(msg, args...)
13}
14
15func Debug(msg string, args ...any) {
16	slog.Debug(msg, args...)
17}
18
19func Warn(msg string, args ...any) {
20	slog.Warn(msg, args...)
21}
22
23func Error(msg string, args ...any) {
24	slog.Error(msg, args...)
25}
26
27func InfoPersist(msg string, args ...any) {
28	args = append(args, persistKeyArg, true)
29	slog.Info(msg, args...)
30}
31
32func DebugPersist(msg string, args ...any) {
33	args = append(args, persistKeyArg, true)
34	slog.Debug(msg, args...)
35}
36
37func WarnPersist(msg string, args ...any) {
38	args = append(args, persistKeyArg, true)
39	slog.Warn(msg, args...)
40}
41
42func ErrorPersist(msg string, args ...any) {
43	args = append(args, persistKeyArg, true)
44	slog.Error(msg, args...)
45}
46
47// RecoverPanic is a common function to handle panics gracefully.
48// It logs the error, creates a panic log file with stack trace,
49// and executes an optional cleanup function before returning.
50func RecoverPanic(name string, cleanup func()) {
51	if r := recover(); r != nil {
52		// Log the panic
53		ErrorPersist(fmt.Sprintf("Panic in %s: %v", name, r))
54
55		// Create a timestamped panic log file
56		timestamp := time.Now().Format("20060102-150405")
57		filename := fmt.Sprintf("crush-panic-%s-%s.log", name, timestamp)
58
59		file, err := os.Create(filename)
60		if err != nil {
61			ErrorPersist(fmt.Sprintf("Failed to create panic log: %v", err))
62		} else {
63			defer file.Close()
64
65			// Write panic information and stack trace
66			fmt.Fprintf(file, "Panic in %s: %v\n\n", name, r)
67			fmt.Fprintf(file, "Time: %s\n\n", time.Now().Format(time.RFC3339))
68			fmt.Fprintf(file, "Stack Trace:\n%s\n", debug.Stack())
69
70			InfoPersist(fmt.Sprintf("Panic details written to %s", filename))
71		}
72
73		// Execute cleanup function if provided
74		if cleanup != nil {
75			cleanup()
76		}
77	}
78}