From 356c890c32ffd7d4799a70fcc5279430b216d8e3 Mon Sep 17 00:00:00 2001 From: reugn Date: Sun, 22 Oct 2023 16:30:57 +0300 Subject: [PATCH 1/2] fix: alternative Logger utilization --- quartz/logger/default_logger.go | 34 +++++++++--- quartz/logger/logger_test.go | 98 +++++++++++++++++++++++++++++++-- 2 files changed, 121 insertions(+), 11 deletions(-) diff --git a/quartz/logger/default_logger.go b/quartz/logger/default_logger.go index 5bc1e99..ebf3917 100644 --- a/quartz/logger/default_logger.go +++ b/quartz/logger/default_logger.go @@ -3,25 +3,45 @@ package logger import ( "log" "os" - "sync/atomic" + "sync" ) -var defaultLogger atomic.Value +type loggerValue struct { + sync.RWMutex + logger Logger +} + +func (l *loggerValue) getLogger() Logger { + l.RLock() + defer l.RUnlock() + return l.logger +} + +func (l *loggerValue) setLogger(new Logger) { + l.Lock() + defer l.Unlock() + l.logger = new +} + +var defaultLogger *loggerValue func init() { - defaultLogger.Store(NewSimpleLogger( - log.New(os.Stdout, "", log.LstdFlags|log.Lshortfile), LevelInfo), - ) + defaultLogger = &loggerValue{ + logger: NewSimpleLogger( + log.New(os.Stdout, "", log.LstdFlags|log.Lshortfile), + LevelInfo, + ), + } } // Default returns the default Logger. func Default() Logger { - return defaultLogger.Load().(Logger) + return defaultLogger.getLogger() } // SetDefault makes l the default Logger. func SetDefault(l Logger) { - defaultLogger.Store(l) + defaultLogger.setLogger(l) } // Trace logs at LevelTrace. diff --git a/quartz/logger/logger_test.go b/quartz/logger/logger_test.go index 605ec69..b78daf3 100644 --- a/quartz/logger/logger_test.go +++ b/quartz/logger/logger_test.go @@ -5,6 +5,7 @@ import ( "io" "log" "strings" + "sync" "testing" "github.com/reugn/go-quartz/quartz/logger" @@ -47,7 +48,7 @@ func TestLoggerOff(t *testing.T) { logger.SetDefault(logger.NewSimpleLogger(stdLogger, logger.LevelOff)) if logger.Enabled(logger.LevelError) { - t.Fatal("LevelError is enabled") + t.Fatal("logger.LevelError is enabled") } logger.Error("Error") assertEmpty(&b, t) @@ -55,6 +56,45 @@ func TestLoggerOff(t *testing.T) { assertEmpty(&b, t) } +func TestLoggerRace(t *testing.T) { + var b bytes.Buffer + stdLogger := log.New(&b, "", log.LstdFlags) + + logger1 := logger.NewSimpleLogger(stdLogger, logger.LevelOff) + logger2 := logger.NewSimpleLogger(stdLogger, logger.LevelTrace) + logger3 := logger.NewSimpleLogger(stdLogger, logger.LevelDebug) + + wg := sync.WaitGroup{} + wg.Add(3) + go setLogger(&wg, logger1) + go setLogger(&wg, logger2) + go setLogger(&wg, logger3) + wg.Wait() + wg.Add(1) + go setLogger(&wg, logger2) + wg.Wait() + + if logger.Default() != logger2 { + t.Fatal("logger set race error") + } +} + +func setLogger(wg *sync.WaitGroup, l *logger.SimpleLogger) { + defer wg.Done() + logger.SetDefault(l) +} + +func TestCustomLogger(t *testing.T) { + l := &countingLogger{} + logger.SetDefault(l) + logger.Debug("debug") + logger.Info("info") + logger.Error("error") + if l.Count != 3 { + t.Fatal("custom logger error") + } +} + func TestLogFormat(t *testing.T) { var b bytes.Buffer stdLogger := log.New(&b, "", log.LstdFlags) @@ -95,21 +135,21 @@ func TestLogFormat(t *testing.T) { func assertEmpty(r io.Reader, t *testing.T) { logMsg := readAll(r, t) if logMsg != "" { - t.Fatalf("Log msg is not empty: %s", logMsg) + t.Fatalf("log msg is not empty: %s", logMsg) } } func assertNotEmpty(r io.Reader, t *testing.T) { logMsg := readAll(r, t) if logMsg == "" { - t.Fatal("Log msg is empty") + t.Fatal("log msg is empty") } } func checkLogFormat(r io.Reader, t *testing.T) { logMsg := readAll(r, t) if !strings.Contains(logMsg, "a, 1, true, {}") { - t.Fatalf("Invalid log format: %s", logMsg) + t.Fatalf("invalid log format: %s", logMsg) } } @@ -120,3 +160,53 @@ func readAll(r io.Reader, t *testing.T) string { } return string(bytes) } + +type countingLogger struct { + Count int +} + +var _ logger.Logger = (*countingLogger)(nil) + +func (l *countingLogger) Trace(_ any) { + l.Count++ +} + +func (l *countingLogger) Tracef(_ string, _ ...any) { + l.Count++ +} + +func (l *countingLogger) Debug(_ any) { + l.Count++ +} + +func (l *countingLogger) Debugf(_ string, _ ...any) { + l.Count++ +} + +func (l *countingLogger) Info(_ any) { + l.Count++ +} + +func (l *countingLogger) Infof(_ string, _ ...any) { + l.Count++ +} + +func (l *countingLogger) Warn(_ any) { + l.Count++ +} + +func (l *countingLogger) Warnf(_ string, _ ...any) { + l.Count++ +} + +func (l *countingLogger) Error(_ any) { + l.Count++ +} + +func (l *countingLogger) Errorf(_ string, _ ...any) { + l.Count++ +} + +func (l *countingLogger) Enabled(_ logger.Level) bool { + return true +} From 5dc4e3745a6a0b0b45c66958fcde8091ae430536 Mon Sep 17 00:00:00 2001 From: reugn Date: Mon, 23 Oct 2023 09:24:25 +0300 Subject: [PATCH 2/2] initialize defaultLogger directly --- quartz/logger/default_logger.go | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/quartz/logger/default_logger.go b/quartz/logger/default_logger.go index ebf3917..62a8677 100644 --- a/quartz/logger/default_logger.go +++ b/quartz/logger/default_logger.go @@ -23,15 +23,11 @@ func (l *loggerValue) setLogger(new Logger) { l.logger = new } -var defaultLogger *loggerValue - -func init() { - defaultLogger = &loggerValue{ - logger: NewSimpleLogger( - log.New(os.Stdout, "", log.LstdFlags|log.Lshortfile), - LevelInfo, - ), - } +var defaultLogger = loggerValue{ + logger: NewSimpleLogger( + log.New(os.Stdout, "", log.LstdFlags|log.Lshortfile), + LevelInfo, + ), } // Default returns the default Logger.