Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 20 additions & 5 deletions zapcore/lazy_with.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ package zapcore
import "sync"

type lazyWithCore struct {
Core
core Core
sync.Once
fields []Field
}
Expand All @@ -32,23 +32,38 @@ type lazyWithCore struct {
// the logger is written to (or is further chained in a lon-lazy manner).
func NewLazyWith(core Core, fields []Field) Core {
return &lazyWithCore{
Core: core,
core: core,
fields: fields,
}
}

func (d *lazyWithCore) initOnce() {
d.Once.Do(func() {
d.Core = d.Core.With(d.fields)
d.core = d.core.With(d.fields)
})
}

func (d *lazyWithCore) With(fields []Field) Core {
d.initOnce()
return d.Core.With(fields)
return d.core.With(fields)
}

func (d *lazyWithCore) Check(e Entry, ce *CheckedEntry) *CheckedEntry {
d.initOnce()
return d.Core.Check(e, ce)
return d.core.Check(e, ce)
}

func (d *lazyWithCore) Enabled(level Level) bool {
d.initOnce()
return d.core.Enabled(level)
}

func (d *lazyWithCore) Write(e Entry, fields []Field) error {
d.initOnce()
return d.core.Write(e, fields)
}

func (d *lazyWithCore) Sync() error {
d.initOnce()
return d.core.Sync()
}
32 changes: 32 additions & 0 deletions zapcore/lazy_with_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
package zapcore_test

import (
"sync"
"sync/atomic"
"testing"

Expand Down Expand Up @@ -152,3 +153,34 @@ func TestLazyCore(t *testing.T) {
})
}
}

// TestLazyCoreRace tests concurrent access to lazyWithCore methods
// This is a regression test for issue #1426
func TestLazyCoreRace(t *testing.T) {
core, _ := observer.New(zapcore.InfoLevel)
lazyCore := zapcore.NewLazyWith(core, []zapcore.Field{
makeInt64Field("lazy", 42),
})

var wg sync.WaitGroup
const numGoroutines = 50

// Test concurrent access to Enabled() method which was the source of the race
for i := 0; i < numGoroutines; i++ {
wg.Add(1)
go func() {
defer wg.Done()

// These operations should not race
_ = lazyCore.Enabled(zapcore.InfoLevel)
_ = lazyCore.Enabled(zapcore.DebugLevel)

// Also test other methods for good measure
if ce := lazyCore.Check(zapcore.Entry{Level: zapcore.InfoLevel, Message: "test"}, nil); ce != nil {
_ = lazyCore.Write(zapcore.Entry{Level: zapcore.InfoLevel, Message: "test"}, nil)
}
}()
}

wg.Wait()
}