From 8b78affe6b4495e04b1b226b4176768dac156ca5 Mon Sep 17 00:00:00 2001 From: Andy Pan Date: Thu, 20 Jul 2023 22:17:37 +0800 Subject: [PATCH] bug: make logging package concurrent-safe Fixes #486 --- gnet_test.go | 28 ++++++++++++++++++++++++++++ pkg/logging/logger.go | 34 ++++++++++++++++++++++++---------- 2 files changed, 52 insertions(+), 10 deletions(-) diff --git a/gnet_test.go b/gnet_test.go index 22eae807b..91391fa65 100644 --- a/gnet_test.go +++ b/gnet_test.go @@ -17,6 +17,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap" + "golang.org/x/sync/errgroup" gerr "github.com/panjf2000/gnet/v2/pkg/errors" "github.com/panjf2000/gnet/v2/pkg/logging" @@ -1135,6 +1136,33 @@ func (s *testClosedWakeUpServer) OnClose(Conn, error) (action Action) { return } +type testMultiInstLoggerRaceServer struct { + *BuiltinEventEngine +} + +func (t *testMultiInstLoggerRaceServer) OnBoot(_ Engine) (action Action) { + return Shutdown +} + +func TestMultiInstLoggerRace(t *testing.T) { + logger1, _ := zap.NewDevelopment() + events1 := new(testMultiInstLoggerRaceServer) + g := errgroup.Group{} + g.Go(func() error { + err := Run(events1, "tulip://howdy", WithLogger(logger1.Sugar())) + return err + }) + + logger2, _ := zap.NewDevelopment() + events2 := new(testMultiInstLoggerRaceServer) + g.Go(func() error { + err := Run(events2, "tulip://howdy", WithLogger(logger2.Sugar())) + return err + }) + + assert.Error(t, g.Wait()) +} + var errIncompletePacket = errors.New("incomplete packet") type simServer struct { diff --git a/pkg/logging/logger.go b/pkg/logging/logger.go index 784aba2ec..86ad17e93 100644 --- a/pkg/logging/logger.go +++ b/pkg/logging/logger.go @@ -50,6 +50,7 @@ import ( "errors" "os" "strconv" + "strings" "sync" "go.uber.org/zap" @@ -63,6 +64,7 @@ import ( type Flusher = func() error var ( + mu sync.RWMutex defaultLogger Logger defaultLoggingLevel Level defaultFlusher Flusher @@ -171,30 +173,28 @@ func getProdEncoder() zapcore.Encoder { // GetDefaultLogger returns the default logger. func GetDefaultLogger() Logger { + mu.RLock() + defer mu.RUnlock() return defaultLogger } // GetDefaultFlusher returns the default flusher. func GetDefaultFlusher() Flusher { + mu.RLock() + defer mu.RUnlock() return defaultFlusher } -var setupOnce sync.Once - // SetDefaultLoggerAndFlusher sets the default logger and its flusher. -// -// Note that this function should only be called once at the -// start of the program and not thereafter for the entire runtime, -// otherwise it will only keep the first setup. func SetDefaultLoggerAndFlusher(logger Logger, flusher Flusher) { - setupOnce.Do(func() { - defaultLogger, defaultFlusher = logger, flusher - }) + mu.Lock() + defaultLogger, defaultFlusher = logger, flusher + mu.Unlock() } // LogLevel tells what the default logging level is. func LogLevel() string { - return defaultLoggingLevel.String() + return strings.ToUpper(defaultLoggingLevel.String()) } // CreateLoggerAsLocalFile setups the logger by local file path. @@ -227,41 +227,55 @@ func CreateLoggerAsLocalFile(localFilePath string, logLevel Level) (logger Logge // Cleanup does something windup for logger, like closing, flushing, etc. func Cleanup() { + mu.RLock() if defaultFlusher != nil { _ = defaultFlusher() } + mu.RUnlock() } // Error prints err if it's not nil. func Error(err error) { if err != nil { + mu.RLock() defaultLogger.Errorf("error occurs during runtime, %v", err) + mu.RUnlock() } } // Debugf logs messages at DEBUG level. func Debugf(format string, args ...interface{}) { + mu.RLock() defaultLogger.Debugf(format, args...) + mu.RUnlock() } // Infof logs messages at INFO level. func Infof(format string, args ...interface{}) { + mu.RLock() defaultLogger.Infof(format, args...) + mu.RUnlock() } // Warnf logs messages at WARN level. func Warnf(format string, args ...interface{}) { + mu.RLock() defaultLogger.Warnf(format, args...) + mu.RUnlock() } // Errorf logs messages at ERROR level. func Errorf(format string, args ...interface{}) { + mu.RLock() defaultLogger.Errorf(format, args...) + mu.RUnlock() } // Fatalf logs messages at FATAL level. func Fatalf(format string, args ...interface{}) { + mu.RLock() defaultLogger.Fatalf(format, args...) + mu.RUnlock() } // Logger is used for logging formatted messages.