diff options
-rw-r--r-- | helpers/general.go | 5 | ||||
-rw-r--r-- | helpers/general_test.go | 55 |
2 files changed, 58 insertions, 2 deletions
diff --git a/helpers/general.go b/helpers/general.go index 236a763fa..472ebc4a4 100644 --- a/helpers/general.go +++ b/helpers/general.go @@ -360,9 +360,10 @@ func (l *DistinctLogger) printIfNotPrinted(level, logStatement string, print fun return } l.Lock() + defer l.Unlock() + l.m[key] = true // Placing this after print() can cause duplicate warning entries to be logged when --panicOnWarning is true. print() - l.m[key] = true - l.Unlock() + } // NewDistinctErrorLogger creates a new DistinctLogger that logs ERRORs diff --git a/helpers/general_test.go b/helpers/general_test.go index 7fe00c51a..be9834d3f 100644 --- a/helpers/general_test.go +++ b/helpers/general_test.go @@ -18,6 +18,7 @@ import ( "reflect" "strings" "testing" + "time" "github.com/gohugoio/hugo/config" @@ -58,6 +59,60 @@ func TestResolveMarkup(t *testing.T) { } } +func TestDistinctLoggerDoesNotLockOnWarningPanic(t *testing.T) { + // Testing to make sure logger mutex doesn't lock if warnings cause panics. + // func Warnf() of DistinctLogger is defined in general.go + l := NewDistinctLogger(loggers.NewWarningLogger()) + + // Set PanicOnWarning to true to reproduce issue 9380 + // Ensure global variable loggers.PanicOnWarning is reset to old value after test + if loggers.PanicOnWarning == false { + loggers.PanicOnWarning = true + defer func() { + loggers.PanicOnWarning = false + }() + } + + // Establish timeout in case a lock occurs: + timeIsUp := make(chan bool) + timeOutSeconds := 1 + go func() { + time.Sleep(time.Second * time.Duration(timeOutSeconds)) + timeIsUp <- true + }() + + // Attempt to run multiple logging threads in parallel + counterC := make(chan int) + goroutines := 5 + + for i := 0; i < goroutines; i++ { + go func() { + defer func() { + // Intentional panic successfully recovered - notify counter channel + recover() + counterC <- 1 + }() + + l.Warnf("Placeholder template message: %v", "In this test, logging a warning causes a panic.") + }() + } + + // All goroutines should complete before timeout + var counter int + for { + select { + case <-counterC: + counter++ + if counter == goroutines { + return + } + case <-timeIsUp: + t.Errorf("Unable to log warnings with --panicOnWarning within alloted time of: %v seconds. Investigate possible mutex locking on panic in distinct warning logger.", timeOutSeconds) + return + } + } +} + func TestFirstUpper(t *testing.T) { for i, this := range []struct { in string |