diff --git a/error_reporting.go b/error_reporting.go index aad1f7f4..140165ae 100644 --- a/error_reporting.go +++ b/error_reporting.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "net/http" + "regexp" "sync" "cloud.google.com/go/errorreporting" @@ -13,7 +14,7 @@ type ErrorReporter interface { Close() error FlushReports() Report(errReport ErrorReport) - WrapLogger(logging Logging, errorAffectsLabel string) Logging + WrapLogger(logging Logging, errorAffectsLabel string, ignoredCriticalfPatterns []*regexp.Regexp) Logging } type googleErrorReporter struct { @@ -71,12 +72,13 @@ func (g *googleErrorReporter) Close() error { } type errorForwardingLogger struct { - wrappedLogger Logging - errorReporter ErrorReporter - errorAffectsLabel string - labelsLock *sync.RWMutex - labels map[string]string - req *http.Request + wrappedLogger Logging + errorReporter ErrorReporter + errorAffectsLabel string + ignoredCriticalfPatterns []*regexp.Regexp + labelsLock *sync.RWMutex + labels map[string]string + req *http.Request } func (e errorForwardingLogger) Debugf(format string, args ...interface{}) { @@ -135,6 +137,14 @@ func (f forwardedError) Error() string { func (e *errorForwardingLogger) forwardError(err error) { var affects string + if len(e.ignoredCriticalfPatterns) > 0 { + errString := err.Error() + for _, pattern := range e.ignoredCriticalfPatterns { + if pattern.MatchString(errString) { + return + } + } + } if len(e.errorAffectsLabel) > 0 { e.labelsLock.RLock() affects = e.labels[e.errorAffectsLabel] @@ -150,15 +160,16 @@ func (e *errorForwardingLogger) forwardError(err error) { /** * Unlike NewGoogleErrorReporter this function may be used liberally, and across threads/requests */ -func (g *googleErrorReporter) WrapLogger(logging Logging, errorAffectsLabel string) Logging { +func (g *googleErrorReporter) WrapLogger(logging Logging, errorAffectsLabel string, ignoredCriticalfPatterns []*regexp.Regexp) Logging { if _, alreadyWrapped := logging.(*errorForwardingLogger); alreadyWrapped { panic("bug! this logger is already wrapped!") } return &errorForwardingLogger{ - wrappedLogger: logging, - errorReporter: g, - errorAffectsLabel: errorAffectsLabel, - labelsLock: &sync.RWMutex{}, - labels: make(map[string]string), + wrappedLogger: logging, + errorReporter: g, + errorAffectsLabel: errorAffectsLabel, + ignoredCriticalfPatterns: ignoredCriticalfPatterns, + labelsLock: &sync.RWMutex{}, + labels: make(map[string]string), } } diff --git a/error_reporting_mock.go b/error_reporting_mock.go index 50cc5909..8f3f79f6 100644 --- a/error_reporting_mock.go +++ b/error_reporting_mock.go @@ -1,6 +1,10 @@ package appwrap -import "github.com/stretchr/testify/mock" +import ( + "regexp" + + "github.com/stretchr/testify/mock" +) type ErrorReporterMock struct { mock.Mock @@ -19,7 +23,7 @@ func (m *ErrorReporterMock) Close() error { return args.Error(0) } -func (m *ErrorReporterMock) WrapLogger(logging Logging, errorAffectsLabel string) Logging { - args := m.Called(logging, errorAffectsLabel) +func (m *ErrorReporterMock) WrapLogger(logging Logging, errorAffectsLabel string, ignoredCriticalfPatterns []*regexp.Regexp) Logging { + args := m.Called(logging, errorAffectsLabel, ignoredCriticalfPatterns) return args.Get(0).(Logging) } diff --git a/error_reporting_test.go b/error_reporting_test.go index 34aa47fd..b8de221b 100644 --- a/error_reporting_test.go +++ b/error_reporting_test.go @@ -3,6 +3,7 @@ package appwrap import ( "errors" "net/http" + "regexp" "sync" "cloud.google.com/go/errorreporting" @@ -72,7 +73,7 @@ func (e *ErrorReportingTest) TestWrapLogger_CantWrapAgain(c *C) { reporter := &googleErrorReporter{} assert.Panics(c, func() { - _ = reporter.WrapLogger(log, "") + _ = reporter.WrapLogger(log, "", nil) }) } @@ -83,7 +84,7 @@ func (e *ErrorReportingTest) TestDebugf_OnlyForwards(c *C) { wrapMe.On("Debugf", "winner, winner, chicken din%s", "ner") - wrappedLog := reporter.WrapLogger(wrapMe, "") + wrappedLog := reporter.WrapLogger(wrapMe, "", nil) wrappedLog.Debugf("winner, winner, chicken din%s", "ner") @@ -98,7 +99,7 @@ func (e *ErrorReportingTest) TestInfof_OnlyForwards(c *C) { wrapMe.On("Infof", "winner, winner, chicken din%s", "ner") - wrappedLog := reporter.WrapLogger(wrapMe, "") + wrappedLog := reporter.WrapLogger(wrapMe, "", nil) wrappedLog.Infof("winner, winner, chicken din%s", "ner") @@ -113,7 +114,7 @@ func (e *ErrorReportingTest) TestWarningf_OnlyForwards(c *C) { wrapMe.On("Warningf", "winner, winner, chicken din%s", "ner") - wrappedLog := reporter.WrapLogger(wrapMe, "") + wrappedLog := reporter.WrapLogger(wrapMe, "", nil) wrappedLog.Warningf("winner, winner, chicken din%s", "ner") @@ -162,6 +163,36 @@ func (e *ErrorReportingTest) TestCriticalf_ForwardsAndReports(c *C) { mockReporter.AssertExpectations(c) } +func (e *ErrorReportingTest) TestCriticalf_Ignores(c *C) { + wrapMe := &LoggingMock{Log: &NullLogger{}} + mockReporter := &ErrorReporterMock{} + forwardLog := &errorForwardingLogger{ + wrappedLogger: wrapMe, + errorReporter: mockReporter, + errorAffectsLabel: "", + ignoredCriticalfPatterns: []*regexp.Regexp{ + regexp.MustCompile(".*context canceled.*"), + }, + labelsLock: &sync.RWMutex{}, + labels: make(map[string]string), + } + contextErr := "this says context canceled" + notIgnoredErr := "show me this error" + + wrapMe.On("Criticalf", notIgnoredErr) + wrapMe.On("Criticalf", contextErr) + mockReporter.On("Report", ErrorReport{ + Err: forwardedError{msg: notIgnoredErr}, + Req: nil, + ErrorAffectsKey: "", + }) + forwardLog.Criticalf(notIgnoredErr) + forwardLog.Criticalf(contextErr) + + wrapMe.AssertExpectations(c) + mockReporter.AssertExpectations(c) +} + func (e *ErrorReportingTest) TestAddLabels_ErrorAffectsKeyIsSetToValue(c *C) { wrapMe := &LoggingMock{Log: &NullLogger{}} mockReporter := &ErrorReporterMock{}