Skip to content

Commit

Permalink
Add a test for concurrent use of global loggers
Browse files Browse the repository at this point in the history
In working on UberFX, I realized that zap's global loggers are actually safe to
replace at runtime; because they're pointers, they're a single machine word.
Since loggers are also never mutated, we don't need any locks to make this code
race-free.

This commit removes the incorrect (and scary) documentation about races and adds
a test that attempts to trigger a race.
  • Loading branch information
Akshay Shah committed Feb 18, 2017
1 parent 9c5f3d4 commit 9b42938
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 7 deletions.
9 changes: 2 additions & 7 deletions global.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,6 @@ import (
var (
// L is a global Logger. It defaults to a no-op implementation but can be
// replaced using ReplaceGlobals.
//
// Both L and S are unsynchronized, so replacing them while they're in
// use isn't safe.
L = New(nil)
// S is a global SugaredLogger, similar to L. It also defaults to a no-op
// implementation.
Expand All @@ -40,11 +37,9 @@ var (

// ReplaceGlobals replaces the global Logger L and the global SugaredLogger S,
// and returns a function to restore the original values.
//
// Note that replacing the global loggers isn't safe while they're being used;
// in practice, this means that only the owner of the application's main
// function should use this method.
func ReplaceGlobals(logger *Logger) func() {
// This doesn't require synchronization to be safe, since pointers are a
// single word.
prev := *L
L = logger
S = logger.Sugar()
Expand Down
32 changes: 32 additions & 0 deletions global_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,15 @@ package zap

import (
"log"
"sync"
"testing"
"time"

"github.com/stretchr/testify/assert"
"go.uber.org/atomic"

"go.uber.org/zap/internal/observer"
"go.uber.org/zap/testutils"
"go.uber.org/zap/zapcore"
)

Expand Down Expand Up @@ -59,6 +63,34 @@ func TestReplaceGlobals(t *testing.T) {
assert.Equal(t, initialS, *S, "Expected func returned from ReplaceGlobals to restore initial S.")
}

func TestGlobalsConcurrentUse(t *testing.T) {
var (
stop atomic.Bool
wg sync.WaitGroup
)

for i := 0; i < 100; i++ {
wg.Add(2)
go func() {
for stop.Load() {
ReplaceGlobals(New(nil))
}
wg.Done()
}()
go func() {
for stop.Load() {
L.Info("")
S.Info("")
}
wg.Done()
}()
}

testutils.Sleep(100 * time.Millisecond)
stop.Toggle()
wg.Wait()
}

func TestRedirectStdLog(t *testing.T) {
initialFlags := log.Flags()
initialPrefix := log.Prefix()
Expand Down

0 comments on commit 9b42938

Please sign in to comment.