diff --git a/pkg/core/error.go b/pkg/core/error.go index f560051..e9ec161 100644 --- a/pkg/core/error.go +++ b/pkg/core/error.go @@ -16,6 +16,7 @@ import ( "runtime" "runtime/debug" "strings" + "sync" "time" ) @@ -390,14 +391,21 @@ func (h *ErrPan) Reports(n int) ([]CrashReport, error) { return reports[len(reports)-n:], nil } -func (h *ErrPan) appendReport(report CrashReport) { - var reports []CrashReport +var crashMu sync.Mutex +func (h *ErrPan) appendReport(report CrashReport) { + crashMu.Lock() + defer crashMu.Unlock() + + var reports []CrashReport if data, err := os.ReadFile(h.filePath); err == nil { - json.Unmarshal(data, &reports) + if err := json.Unmarshal(data, &reports); err != nil { + reports = nil + } } reports = append(reports, report) - data, _ := json.MarshalIndent(reports, "", " ") - os.WriteFile(h.filePath, data, 0644) + if data, err := json.MarshalIndent(reports, "", " "); err == nil { + _ = os.WriteFile(h.filePath, data, 0600) + } } diff --git a/pkg/core/log.go b/pkg/core/log.go index 75bef44..3b66599 100644 --- a/pkg/core/log.go +++ b/pkg/core/log.go @@ -12,6 +12,7 @@ import ( "os/user" "slices" "sync" + "sync/atomic" "time" ) @@ -175,6 +176,9 @@ func (l *Log) log(level Level, prefix, msg string, keyvals ...any) { timestamp := styleTimestamp(time.Now().Format("15:04:05")) + // Copy keyvals to avoid mutating the caller's slice + keyvals = append([]any(nil), keyvals...) + // Automatically extract context from error if present in keyvals origLen := len(keyvals) for i := 0; i < origLen; i += 2 { @@ -294,16 +298,23 @@ func Username() string { // --- Default logger --- -var defaultLog = NewLog(LogOpts{Level: LevelInfo}) +var defaultLogPtr atomic.Pointer[Log] + +func init() { + l := NewLog(LogOpts{Level: LevelInfo}) + defaultLogPtr.Store(l) +} + +var defaultLog = defaultLogPtr.Load() // Default returns the default logger. func Default() *Log { - return defaultLog + return defaultLogPtr.Load() } -// SetDefault sets the default logger. +// SetDefault sets the default logger (thread-safe). func SetDefault(l *Log) { - defaultLog = l + defaultLogPtr.Store(l) } // SetLevel sets the default logger's level.