Skip to content

fix: UpdateContext skips Nop and zero-value loggers#754

Open
veeceey wants to merge 2 commits intors:masterfrom
veeceey:fix-update-context-disabled-check
Open

fix: UpdateContext skips Nop and zero-value loggers#754
veeceey wants to merge 2 commits intors:masterfrom
veeceey:fix-update-context-disabled-check

Conversation

@veeceey
Copy link

@veeceey veeceey commented Feb 10, 2026

Summary

Fixes #643.

UpdateContext used pointer equality (l == disabledLogger) to detect disabled loggers. This only caught the package-level disabledLogger singleton returned by Ctx() when no logger is in the context, but missed loggers created via:

  • zerolog.Nop() (returns a new disabled logger each time)
  • Zero-value Logger{} (has nil writer)

When a shared Nop() logger was used from multiple goroutines, UpdateContext would proceed to mutate the context buffer concurrently, causing data races (confirmed with -race detector, reproduces 15 race warnings).

Changes

  • log.go: Replace l == disabledLogger with l.w == nil || l.level == Disabled, consistent with how Logger.should() determines if logging is active
  • log_test.go: Add TestUpdateContextOnNopLogger and TestUpdateContextOnZeroValueLogger to cover both missed cases

Test plan

  • All existing tests pass (go test ./...)
  • New tests verify UpdateContext is a no-op on Nop() loggers
  • New tests verify UpdateContext is a no-op on zero-value loggers
  • Data race no longer reproduces with go run -race on shared Nop() logger

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>
@veeceey
Copy link
Author

veeceey commented Feb 19, 2026

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!

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.UpdateContext to skip updates when l.w == nil or l.level == Disabled.
  • Add tests covering UpdateContext behavior on Nop() 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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
	}
...

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.
Copy link
Contributor

@IDisposable IDisposable left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:shipit:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

logger.UpdateContext checks for disabled logger using equality

3 participants