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}