diff --git a/router/errors.go b/router/errors.go index 82800fd..5de1b63 100644 --- a/router/errors.go +++ b/router/errors.go @@ -8,16 +8,18 @@ import ( "github.com/bugsnag/bugsnag-go/v2" "github.com/netlify/netlify-commons/metriks" "github.com/netlify/netlify-commons/tracing" + "github.com/sirupsen/logrus" ) // HTTPError is an error with a message and an HTTP status code. type HTTPError struct { - Code int `json:"code"` - Message string `json:"msg"` - JSON interface{} `json:"json,omitempty"` - InternalError error `json:"-"` - InternalMessage string `json:"-"` - ErrorID string `json:"error_id,omitempty"` + Code int `json:"code"` + Message string `json:"msg"` + JSON interface{} `json:"json,omitempty"` + InternalError error `json:"-"` + InternalMessage string `json:"-"` + ErrorID string `json:"error_id,omitempty"` + Fields logrus.Fields `json:"-"` } // BadRequestError creates a 400 HTTP error @@ -79,10 +81,25 @@ func (e *HTTPError) WithInternalMessage(fmtString string, args ...interface{}) * return e } +// WithFields will add fields to an error message +func (e *HTTPError) WithFields(fields logrus.Fields) *HTTPError { + for key, value := range fields { + e.Fields[key] = value + } + return e +} + +// WithFields will add fields to an error message +func (e *HTTPError) WithField(key string, value interface{}) *HTTPError { + e.Fields[key] = value + return e +} + func httpError(code int, fmtString string, args ...interface{}) *HTTPError { return &HTTPError{ Code: code, Message: fmt.Sprintf(fmtString, args...), + Fields: make(logrus.Fields), } } @@ -102,6 +119,8 @@ func HandleError(err error, w http.ResponseWriter, r *http.Request) { switch e := err.(type) { case *HTTPError: + log = log.WithFields(e.Fields) + e.ErrorID = errorID if e.Code >= http.StatusInternalServerError { notifyBugsnag = true diff --git a/router/errors_test.go b/router/errors_test.go index 5a762e0..e58a743 100644 --- a/router/errors_test.go +++ b/router/errors_test.go @@ -122,6 +122,36 @@ func TestHandleError_HTTPError(t *testing.T) { assert.Equal(t, "internal server error: "+httpErr.InternalMessage, loggerOutput.AllEntries()[0].Message) } +func TestHandleError_HttpErrorWithFields(t *testing.T) { + logger, loggerOutput := test.NewNullLogger() + recorder := httptest.NewRecorder() + w, r, _ := tracing.NewTracer( + recorder, + httptest.NewRequest(http.MethodGet, "/", nil), + logger, + "test", + "test", + ) + + httpErr := httpError(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) + + httpErr.WithFields(map[string]interface{}{ + "a": "1", + "b": "2", + }) + + httpErr.WithField("c", "3") + httpErr.WithField("a", "0") + + HandleError(httpErr, w, r) + + require.Len(t, loggerOutput.AllEntries(), 1) + entry := loggerOutput.LastEntry() + assert.Equal(t, entry.Data["a"], "0") + assert.Equal(t, entry.Data["b"], "2") + assert.Equal(t, entry.Data["c"], "3") +} + func TestHandleError_NoLogForNormalErrors(t *testing.T) { logger, loggerOutput := test.NewNullLogger() recorder := httptest.NewRecorder()