diff --git a/log_set-loglevel-setter.go b/log_set-loglevel-setter.go index 7c60c01..a587528 100644 --- a/log_set-loglevel-setter.go +++ b/log_set-loglevel-setter.go @@ -77,7 +77,9 @@ func (l *logLevelSetter) Set(ctx context.Context, logLevel glog.Level) error { glog.V(l.defaultLoglevel). Infof("set loglevel to %d and reset in %v back to %d", logLevel, l.autoResetDuration, l.defaultLoglevel) go func() { - ctx, cancel := context.WithTimeout(ctx, l.autoResetDuration) + // Use Background context to ensure reset happens after duration, + // regardless of whether the caller's context is cancelled + ctx, cancel := context.WithTimeout(context.Background(), l.autoResetDuration) defer cancel() <-ctx.Done() @@ -87,8 +89,11 @@ func (l *logLevelSetter) Set(ctx context.Context, logLevel glog.Level) error { } func (l *logLevelSetter) resetLogLevel() { + l.mux.Lock() + defer l.mux.Unlock() + if time.Since(l.lastSetTime) <= l.autoResetDuration { - glog.V(l.defaultLoglevel).Infof("time since lastSet is to short => skip reset loglevel") + glog.V(l.defaultLoglevel).Infof("time since lastSet is too short => skip reset loglevel") return } diff --git a/log_set-loglevel-setter_test.go b/log_set-loglevel-setter_test.go index ab4df5a..607de62 100644 --- a/log_set-loglevel-setter_test.go +++ b/log_set-loglevel-setter_test.go @@ -7,6 +7,7 @@ package log_test import ( "context" "errors" + "time" "github.com/golang/glog" . "github.com/onsi/ginkgo/v2" @@ -23,6 +24,32 @@ var _ = Describe("Log LogLevelSetter", Serial, func() { ctx = context.Background() }) + Context("NewLogLevelSetter", func() { + It("is safe for concurrent use from multiple goroutines", func() { + // This test verifies there are no data races when multiple goroutines + // call Set concurrently. Run with: go test -race ./... + logLevelSetter = log.NewLogLevelSetter(glog.Level(1), 50*time.Millisecond) + + const numGoroutines = 20 + done := make(chan bool, numGoroutines) + + for i := 0; i < numGoroutines; i++ { + go func(level int) { + defer func() { done <- true }() + _ = logLevelSetter.Set(ctx, glog.Level(level%5)) + }(i) + } + + // Wait for all goroutines to complete + for i := 0; i < numGoroutines; i++ { + <-done + } + + // Wait for resets to trigger + time.Sleep(100 * time.Millisecond) + }) + }) + Context("LogLevelSetterFunc", func() { It("calls the wrapped function", func() { called := false