log.go 1.5 KB

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