fix: resolve Gemini review findings — race conditions and error handling
- error.go: appendReport now mutex-protected, handles JSON errors, uses 0600 perms - log.go: keyvals slice copied before mutation to prevent caller data races - log.go: defaultLog uses atomic.Pointer for thread-safe replacement Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
8199727537
commit
2525d10515
2 changed files with 28 additions and 9 deletions
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue