charmbracelet / charmbracelet/log

Wrapped loggers don't inherit level changes

Open
#184 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Go
Stars
3.4k
Forks
103
PR merge metrics
No merged PRs in 30d

Description

## Summary

Wrapped loggers created with `.With()` don't inherit log level changes made after their creation. This causes debug messages from wrapped loggers to be silently dropped even when the global log level is set to debug.

## Expected Behavior

When the global log level is changed, all loggers (including wrapped ones created with `.With()`) should respect the new level setting.

## Actual Behavior

Wrapped loggers created with `.With()` retain the log level that was active at the time of their creation and don't inherit subsequent level changes.

## Proof of Concept

```go
package main

import (
"os"
"github.com/charmbracelet/log"
)

func main() {
logger := log.New(os.Stdout)
logger.SetLevel(log.InfoLevel) // Set to Info initially
logger.SetReportTimestamp(false)
logger.SetReportCaller(false)

// Create wrapped logger BEFORE changing to Debug level
wrappedLogger := logger.With("component", "auth")

// Now set to Debug level
logger.SetLevel(log.DebugLevel)

// Test if both loggers show debug messages
println("=== Testing debug messages ===")
logger.Debug("This shows - original logger")
wrappedLogger.Debug("This doesn't show - wrapped logger")

println("\n=== Testing info messages ===")
logger.Info("This shows - original logger")
wrappedLogger.Info("This shows - wrapped logger")
}
```

**Expected Output:**
```
=== Testing debug messages ===
DEBU This shows - original logger
DEBU This doesn't show - wrapped logger component=auth

=== Testing info messages ===
INFO This shows - original logger
INFO This shows - wrapped logger component=auth
```

**Actual Output:**
```
=== Testing debug messages ===
DEBU This shows - original logger

=== Testing info messages ===
INFO This shows - original logger
INFO This shows - wrapped logger component=auth
```

Notice the wrapped logger's debug message is missing.

## Root Cause

The issue is in the `With()` method in `logger.go`:

```go
func (l *Logger) With(keyvals ...any) *Logger {
var st Styles
l.mu.Lock()
sl := *l // This copies the current level value by value
st = *l.styles
l.mu.Unlock()
// ... rest of method
return &sl
}
```

When `sl := *l` is executed, it copies the current value of the `level` field into a new memory location. The level checking in `Log()` uses `atomic.LoadInt64(&l.level)`, which reads from each logger's individual level field rather than a shared global level.

## Impact

This affects any application that:
1. Creates wrapped loggers early in initialization
2. Changes log levels after wrapped loggers are created (common pattern for CLI tools with verbose flags)
3. Expects debug messages from wrapped loggers to appear when debug mode is enabled

## Possible Solutions

1. **Shared level reference**: Store a pointer to the parent logger's level field instead of copying the value
2. **Level inheritance**: Make wrapped loggers check their parent's level in addition to their own
3. **Documentation**: Clearly document that level changes don't affect existing wrapped loggers

## Environment

- Go version: 1.21+
- charmbracelet/log version: v0.4.2 (likely affects other versions too)

Contributor guide

Open the contributing guide

Research direction

Start in logger.go by reading Logger.With and the level check in Log, then run the proof of concept from the issue to reproduce the missing wrapped debug message. Determine how level changes should propagate without breaking logger state, and consider the shared-reference, parent-check, or documentation options; done means a wrapped logger created before SetLevel respects the later level change.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
observability
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.