1package log
2
3import (
4 "fmt"
5 "log/slog"
6 "os"
7 "runtime/debug"
8 "sync"
9 "sync/atomic"
10 "time"
11
12 "github.com/charmbracelet/crush/internal/event"
13 "gopkg.in/natefinch/lumberjack.v2"
14)
15
16var (
17 initOnce sync.Once
18 initialized atomic.Bool
19)
20
21func Setup(logFile string, debug bool) {
22 initOnce.Do(func() {
23 logRotator := &lumberjack.Logger{
24 Filename: logFile,
25 MaxSize: 10, // Max size in MB
26 MaxBackups: 0, // Number of backups
27 MaxAge: 30, // Days
28 Compress: false, // Enable compression
29 }
30
31 level := slog.LevelInfo
32 if debug {
33 level = slog.LevelDebug
34 }
35
36 logger := slog.NewJSONHandler(logRotator, &slog.HandlerOptions{
37 Level: level,
38 AddSource: true,
39 })
40
41 slog.SetDefault(slog.New(logger))
42 initialized.Store(true)
43 })
44}
45
46func Initialized() bool {
47 return initialized.Load()
48}
49
50func RecoverPanic(name string, cleanup func()) {
51 if r := recover(); r != nil {
52 event.Error(r, "panic", true, "name", name)
53
54 // Create a timestamped panic log file
55 timestamp := time.Now().Format("20060102-150405")
56 filename := fmt.Sprintf("crush-panic-%s-%s.log", name, timestamp)
57
58 file, err := os.Create(filename)
59 if err == nil {
60 defer file.Close()
61
62 // Write panic information and stack trace
63 fmt.Fprintf(file, "Panic in %s: %v\n\n", name, r)
64 fmt.Fprintf(file, "Time: %s\n\n", time.Now().Format(time.RFC3339))
65 fmt.Fprintf(file, "Stack Trace:\n%s\n", debug.Stack())
66
67 // Execute cleanup function if provided
68 if cleanup != nil {
69 cleanup()
70 }
71 }
72 }
73}