fix: UpdateContext skips Nop and zero-value loggers#754
fix: UpdateContext skips Nop and zero-value loggers#754
Conversation
UpdateContext previously used pointer equality (l == disabledLogger) to
detect disabled loggers. This only caught the package-level singleton
returned by Ctx() but missed loggers created via Nop() or zero-value
Logger{}, which could lead to data races when a shared Nop logger was
used from multiple goroutines.
Replace the pointer comparison with a check for nil writer or Disabled
level, consistent with how Logger.should() determines if logging is
active. This prevents unnecessary context mutations on loggers that
will never produce output.
Fixes rs#643
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Hi @rs, friendly ping on this PR. It's been open for about 8 days without any review activity. Would appreciate any feedback when you have a moment. Happy to make adjustments if needed. Thank you! |
There was a problem hiding this comment.
Pull request overview
This PR fixes UpdateContext incorrectly identifying “disabled” loggers using pointer equality, which missed Nop()-created loggers and zero-value Logger{} instances. The update aligns UpdateContext’s early-exit condition with the logger’s enabled/disabled semantics to prevent unintended context mutation (and associated data races) when logging is effectively disabled.
Changes:
- Update
Logger.UpdateContextto skip updates whenl.w == nilorl.level == Disabled. - Add tests covering
UpdateContextbehavior onNop()loggers and zero-value loggers.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| log.go | Updates UpdateContext to detect disabled/no-writer loggers without relying on pointer equality. |
| log_test.go | Adds regression tests ensuring UpdateContext is a no-op for Nop() and zero-value loggers. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
log.go
Outdated
| // Use the With method to create a child logger before modifying the context from concurrent goroutines. | ||
| func (l *Logger) UpdateContext(update func(c Context) Context) { | ||
| if l == disabledLogger { | ||
| if l.w == nil || l.level == Disabled { |
There was a problem hiding this comment.
Might be better to add a method to log.go that encapsulates this and call here and in should()...
// detects if this is a disabled logger
func (l *Logger) disabled() bool {
return l.w == nil || l.level <= Disabled
}then here we do
if l.disabled() {
return
}and then should() starts with
// should returns true if the log event should be logged.
func (l *Logger) should(lvl Level) bool {
if l.disabled() {
return false
}
...There was a problem hiding this comment.
Good call — done. Extracted a disabled() method and updated both UpdateContext and should() to use it.
Extract the disabled logger check into a dedicated disabled() method and use it in both UpdateContext and should() to avoid duplicating the nil writer / Disabled level logic.
Summary
Fixes #643.
UpdateContextused pointer equality (l == disabledLogger) to detect disabled loggers. This only caught the package-leveldisabledLoggersingleton returned byCtx()when no logger is in the context, but missed loggers created via:zerolog.Nop()(returns a new disabled logger each time)Logger{}(has nil writer)When a shared
Nop()logger was used from multiple goroutines,UpdateContextwould proceed to mutate the context buffer concurrently, causing data races (confirmed with-racedetector, reproduces 15 race warnings).Changes
log.go: Replacel == disabledLoggerwithl.w == nil || l.level == Disabled, consistent with howLogger.should()determines if logging is activelog_test.go: AddTestUpdateContextOnNopLoggerandTestUpdateContextOnZeroValueLoggerto cover both missed casesTest plan
go test ./...)UpdateContextis a no-op onNop()loggersUpdateContextis a no-op on zero-value loggersgo run -raceon sharedNop()logger