diff --git a/pkg/models/context.go b/pkg/models/context.go index eaf1bbf2871..c6099f423cc 100644 --- a/pkg/models/context.go +++ b/pkg/models/context.go @@ -1,6 +1,7 @@ package models import ( + "errors" "net/http" "strings" @@ -10,6 +11,7 @@ import ( "github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/setting" + "github.com/grafana/grafana/pkg/util/errutil" "github.com/grafana/grafana/pkg/web" "github.com/prometheus/client_golang/prometheus" ) @@ -86,6 +88,61 @@ func (ctx *ReqContext) JsonApiErr(status int, message string, err error) { ctx.JSON(status, resp) } +// WriteErr writes an error response based on errutil.Error. +// If provided error is not errutil.Error a 500 response is written. +func (ctx *ReqContext) WriteErr(err error) { + ctx.writeErrOrFallback(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError), err) +} + +// WriteErrOrFallback uses the information in an errutil.Error if available +// and otherwise falls back to the status and message provided as arguments. +func (ctx *ReqContext) WriteErrOrFallback(status int, message string, err error) { + ctx.writeErrOrFallback(status, message, err) +} + +func (ctx *ReqContext) writeErrOrFallback(status int, message string, err error) { + data := make(map[string]interface{}) + traceID := tracing.TraceIDFromContext(ctx.Req.Context(), false) + + if err != nil { + data["traceID"] = traceID + + var logMessage string + logger := ctx.Logger.Warn + + var gfErr *errutil.Error + if errors.As(err, gfErr) { + logger = gfErr.LogLevel.LogFunc(ctx.Logger) + publicErr := gfErr.Public() + + // need to manually set these fields because we want to include the trace id + data["extra"] = publicErr.Extra + data["message"] = publicErr.Message + data["messageId"] = publicErr.MessageID + data["statusCode"] = publicErr.StatusCode + } else { + if message != "" { + logMessage = message + } else { + logMessage = http.StatusText(status) + data["message"] = logMessage + } + + if status == http.StatusInternalServerError { + logger = ctx.Logger.Error + } + } + + logger(logMessage, "error", err, "remote_addr", ctx.RemoteAddr(), "traceID", traceID) + } + + if _, ok := data["message"]; !ok && message != "" { + data["message"] = message + } + + ctx.JSON(status, data) +} + func (ctx *ReqContext) HasUserRole(role org.RoleType) bool { return ctx.OrgRole.Includes(role) } diff --git a/pkg/services/contexthandler/auth_jwt.go b/pkg/services/contexthandler/auth_jwt.go index debc247aa95..4386471c12d 100644 --- a/pkg/services/contexthandler/auth_jwt.go +++ b/pkg/services/contexthandler/auth_jwt.go @@ -37,7 +37,7 @@ func (h *ContextHandler) initContextWithJWT(ctx *models.ReqContext, orgId int64) *ctx.Req = *ctx.Req.WithContext(newCtx) if err != nil { - writeErr(ctx, err) + ctx.WriteErr(err) return true } diff --git a/pkg/services/contexthandler/contexthandler.go b/pkg/services/contexthandler/contexthandler.go index 1f84d4484f3..8c04274892c 100644 --- a/pkg/services/contexthandler/contexthandler.go +++ b/pkg/services/contexthandler/contexthandler.go @@ -34,7 +34,6 @@ import ( "github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/util" - "github.com/grafana/grafana/pkg/util/errutil" "github.com/grafana/grafana/pkg/web" ) @@ -287,7 +286,7 @@ func (h *ContextHandler) initContextWithAPIKey(reqContext *models.ReqContext) bo *reqContext.Req = *reqContext.Req.WithContext(ctx) if err != nil { - writeErr(reqContext, err) + reqContext.WriteErr(err) return true } @@ -416,7 +415,7 @@ func (h *ContextHandler) initContextWithBasicAuth(reqContext *models.ReqContext, *reqContext.Req = *reqContext.Req.WithContext(ctx) if err != nil { - writeErr(reqContext, err) + reqContext.WriteErr(err) return true } @@ -640,7 +639,7 @@ func (h *ContextHandler) initContextWithRenderAuth(reqContext *models.ReqContext } if err != nil { - writeErr(reqContext, err) + reqContext.WriteErr(err) return true } @@ -724,7 +723,8 @@ func (h *ContextHandler) initContextWithAuthProxy(reqContext *models.ReqContext, } if err != nil { - writeErr(reqContext, err) + reqContext.WriteErr(err) + return true } ctx := WithAuthHTTPHeader(reqContext.Req.Context(), h.Cfg.AuthProxyHeaderName) @@ -829,16 +829,6 @@ func (h *ContextHandler) initContextWithAuthProxy(reqContext *models.ReqContext, return true } -// writeErr will write error response based on errutil.Error. -func writeErr(c *models.ReqContext, err error) { - grfErr := &errutil.Error{} - if !errors.As(err, grfErr) { - c.JsonApiErr(http.StatusInternalServerError, "", err) - return - } - c.JsonApiErr(grfErr.Reason.Status().HTTPStatus(), grfErr.Public().Message, err) -} - type authHTTPHeaderListContextKey struct{} var authHTTPHeaderListKey = authHTTPHeaderListContextKey{}