[FIXED] SetLoggerV2 deadlocks when prev logger errors on close (#8430)
Calling `s.Errorf(...)` within `s.logging.Lock(); ...;
s.logging.Unlock()` would result in a deadlock, 'cause `s.Errorf()`
tries to acquire the same mutex (`s.logging`).
Reproduction:
```go
// failingCloseLogger implements both server.Logger and io.Closer.
type failingCloseLogger struct{}
func (l *failingCloseLogger) Noticef(format string, v ...any) {}
// ... other server.Logger functions
func (l *failingCloseLogger) Close() error {
return errors.New("simulated close error")
}
func main() {
opts := &server.Options{
Host: "127.0.0.1",
Port: -1,
NoLog: true,
NoSigs: true,
}
s, err := server.NewServer(opts)
if err != nil {
os.Exit(1)
}
s.SetLogger(&failingCloseLogger{}, false, false)
fmt.Println("First logger installed.")
done := make(chan struct{})
go func() {
s.SetLogger(&failingCloseLogger{}, false, false)
close(done)
}()
select {
case <-done:
fmt.Println("Second logger installed.")
case <-time.After(3 * time.Second):
buf := make([]byte, 64<<10)
numBytes := runtime.Stack(buf, true)
fmt.Println(string(buf[:numBytes]))
panic("deadlocked")
}
}
```
Results in:
```sh
$ go run main.go
First logger installed.
goroutine 1 [running]:
main.main()
eadlock-repro/main.go:54 +0x200
goroutine 5 [sync.RWMutex.RLock]:
sync.runtime_SemacquireRWMutexR(0x8adc4b17ee8?, 0xdc?, 0x10?)
/opt/homebrew/Cellar/go/1.26.1/libexec/src/runtime/sema.go:100 +0x28
sync.(*RWMutex).RLock(...)
/opt/homebrew/Cellar/go/1.26.1/libexec/src/sync/rwmutex.go:74
github.com/nats-io/nats-server/v2/server.(*Server).executeLogCall(0x8adc4c5a008, 0x104daab70, {0x1046cccfc, 0x18}, {0x8adc4ad2b20, 0x1, 0x1})
nats-server/server/log.go:288 +0x90
github.com/nats-io/nats-server/v2/server.(*Server).Errorf(...)
nats-server/server/log.go:189
github.com/nats-io/nats-server/v2/server.(*Server).SetLoggerV2(0x8adc4c5a008, {0x104db8088, 0x104e64e00}, 0x0?, 0x0?, 0x0?)
nats-server/server/log.go:139 +0x1e4
github.com/nats-io/nats-server/v2/server.(*Server).SetLogger(...)
nats-server/server/log.go:112
main.main.func1()
main.go:45 +0x44
created by main.main in goroutine 1
main.go:44 +0x150
```
The issue was found with [mulint](https://github.com/palkan/mulint), so
it's kinda hypothetical. Potential real-world scenarios when it might
occur is log rotation (`ReOpenLogs(...)` w/ a broken old logger) and
embedded servers calling `SetLogger` multiple times. N
Neil committed
db0a2c8ce3241df7dbc6cd730b989cfa05677068
Committed by GitHub <noreply@github.com>
on 8/4/2026, 3:38:52 PM