diff --git a/arbnode/batch_poster.go b/arbnode/batch_poster.go index 8885dab13d..4bd0e2490a 100644 --- a/arbnode/batch_poster.go +++ b/arbnode/batch_poster.go @@ -1145,11 +1145,11 @@ func (b *BatchPoster) Start(ctxIn context.Context) { b.redisLock.Start(ctxIn) b.StopWaiter.Start(ctxIn, b) b.LaunchThread(b.pollForReverts) - commonEphemeralErrorHandler := util.NewEphemeralErrorHandler(time.Minute, "", 0, nil) - exceedMaxMempoolSizeEphemeralErrorHandler := util.NewEphemeralErrorHandler(5*time.Minute, dataposter.ErrExceedsMaxMempoolSize.Error(), time.Minute, log.Debug) - storageRaceEphemeralErrorHandler := util.NewEphemeralErrorHandler(5*time.Minute, storage.ErrStorageRace.Error(), time.Minute, log.Debug) - normalGasEstimationFailedEphemeralErrorHandler := util.NewEphemeralErrorHandler(5*time.Minute, ErrNormalGasEstimationFailed.Error(), time.Minute, log.Debug) - accumulatorNotFoundEphemeralErrorHandler := util.NewEphemeralErrorHandler(5*time.Minute, AccumulatorNotFoundErr.Error(), time.Minute, log.Debug) + commonEphemeralErrorHandler := util.NewEphemeralErrorHandler(time.Minute, "", 0) + exceedMaxMempoolSizeEphemeralErrorHandler := util.NewEphemeralErrorHandler(5*time.Minute, dataposter.ErrExceedsMaxMempoolSize.Error(), time.Minute) + storageRaceEphemeralErrorHandler := util.NewEphemeralErrorHandler(5*time.Minute, storage.ErrStorageRace.Error(), time.Minute) + normalGasEstimationFailedEphemeralErrorHandler := util.NewEphemeralErrorHandler(5*time.Minute, ErrNormalGasEstimationFailed.Error(), time.Minute) + accumulatorNotFoundEphemeralErrorHandler := util.NewEphemeralErrorHandler(5*time.Minute, AccumulatorNotFoundErr.Error(), time.Minute) resetAllEphemeralErrs := func() { commonEphemeralErrorHandler.Reset() exceedMaxMempoolSizeEphemeralErrorHandler.Reset() diff --git a/staker/staker.go b/staker/staker.go index 002dd1f754..2a95e9c9f7 100644 --- a/staker/staker.go +++ b/staker/staker.go @@ -402,7 +402,7 @@ func (s *Staker) Start(ctxIn context.Context) { } s.StopWaiter.Start(ctxIn, s) backoff := time.Second - ephemeralErrorHandler := util.NewEphemeralErrorHandler(10*time.Minute, "is ahead of on-chain nonce", 0, nil) + ephemeralErrorHandler := util.NewEphemeralErrorHandler(10*time.Minute, "is ahead of on-chain nonce", 0) s.CallIteratively(func(ctx context.Context) (returningWait time.Duration) { defer func() { panicErr := recover() diff --git a/util/log.go b/util/log.go index 0d87b55977..dbeed8051d 100644 --- a/util/log.go +++ b/util/log.go @@ -18,18 +18,19 @@ type EphemeralErrorHandler struct { IgnoredErrLogLevel func(string, ...interface{}) // Default IgnoredErrLogLevel is log.Debug } -func NewEphemeralErrorHandler(duration time.Duration, errorString string, ignoreDuration time.Duration, ignoredErrLogLevel func(msg string, ctx ...interface{})) *EphemeralErrorHandler { +func NewEphemeralErrorHandler(duration time.Duration, errorString string, ignoreDuration time.Duration) *EphemeralErrorHandler { return &EphemeralErrorHandler{ Duration: duration, ErrorString: errorString, FirstOccurrence: &time.Time{}, IgnoreDuration: ignoreDuration, - IgnoredErrLogLevel: ignoredErrLogLevel, + IgnoredErrLogLevel: log.Debug, } } -// LogLevel method defaults to returning the input currentLogLevel if the givenerror doesnt contain the errorSubstring, +// LogLevel method defaults to returning the input currentLogLevel if the given error doesnt contain the errorSubstring, // but if it does, then returns one of the corresponding loglevels as follows +// - IgnoredErrLogLevel - if the error has been repeating for less than the IgnoreDuration of time. Defaults to log.Debug // - log.Warn - if the error has been repeating for less than the given duration of time // - log.Error - Otherwise // @@ -44,6 +45,10 @@ func (h *EphemeralErrorHandler) LogLevel(err error, currentLogLevel func(msg str return currentLogLevel } + if *h.FirstOccurrence == (time.Time{}) { + *h.FirstOccurrence = time.Now() + } + if h.IgnoreDuration != 0 && time.Since(*h.FirstOccurrence) < h.IgnoreDuration { if h.IgnoredErrLogLevel != nil { return h.IgnoredErrLogLevel @@ -51,14 +56,10 @@ func (h *EphemeralErrorHandler) LogLevel(err error, currentLogLevel func(msg str return log.Debug } - logLevel := log.Error - if *h.FirstOccurrence == (time.Time{}) { - *h.FirstOccurrence = time.Now() - logLevel = log.Warn - } else if time.Since(*h.FirstOccurrence) < h.Duration { - logLevel = log.Warn + if time.Since(*h.FirstOccurrence) < h.Duration { + return log.Warn } - return logLevel + return log.Error } func (h *EphemeralErrorHandler) Reset() { diff --git a/util/log_test.go b/util/log_test.go new file mode 100644 index 0000000000..f8007373f2 --- /dev/null +++ b/util/log_test.go @@ -0,0 +1,70 @@ +package util + +import ( + "errors" + "reflect" + "testing" + "time" + + "github.com/ethereum/go-ethereum/log" +) + +func compareFunctions(f1, f2 func(msg string, ctx ...interface{})) bool { + return reflect.ValueOf(f1).Pointer() == reflect.ValueOf(f2).Pointer() +} +func TestSimple(t *testing.T) { + allErrHandler := NewEphemeralErrorHandler(2500*time.Millisecond, "", time.Second) + err := errors.New("sample error") + logLevel := allErrHandler.LogLevel(err, log.Error) + if !compareFunctions(log.Debug, logLevel) { + t.Fatalf("incorrect loglevel output. Want: Debug") + } + + time.Sleep(1 * time.Second) + logLevel = allErrHandler.LogLevel(err, log.Error) + if !compareFunctions(log.Warn, logLevel) { + t.Fatalf("incorrect loglevel output. Want: Warn") + } + + time.Sleep(2 * time.Second) + logLevel = allErrHandler.LogLevel(err, log.Error) + if !compareFunctions(log.Error, logLevel) { + t.Fatalf("incorrect loglevel output. Want: Error") + } +} + +func TestComplex(t *testing.T) { + // Simulation: errorA happens continuously for 2 seconds and then errorB happens + errorAHandler := NewEphemeralErrorHandler(time.Second, "errorA", 0) + errorBHandler := NewEphemeralErrorHandler(1500*time.Millisecond, "errorB", 0) + + // Computes result of chaining two ephemeral error handlers for a given recurring error + chainingErrHandlers := func(err error) func(string, ...interface{}) { + logLevel := log.Error + logLevel = errorAHandler.LogLevel(err, logLevel) + logLevel = errorBHandler.LogLevel(err, logLevel) + return logLevel + } + + errA := errors.New("this is a sample errorA") + if !compareFunctions(log.Warn, chainingErrHandlers(errA)) { + t.Fatalf("incorrect loglevel output. Want: Warn") + } + time.Sleep(2 * time.Second) + if !compareFunctions(log.Error, chainingErrHandlers(errA)) { + t.Fatalf("incorrect loglevel output. Want: Error") + } + + errB := errors.New("this is a sample errorB") + if !compareFunctions(log.Warn, chainingErrHandlers(errB)) { + t.Fatalf("incorrect loglevel output. Want: Warn") + } + if !compareFunctions(log.Warn, chainingErrHandlers(errA)) { + t.Fatalf("incorrect loglevel output. Want: Warn") + } + + errC := errors.New("random error") + if !compareFunctions(log.Error, chainingErrHandlers(errC)) { + t.Fatalf("incorrect loglevel output. Want: Error") + } +}