Like Prometheus, but for logs.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
loki/pkg/util/server/error.go

88 lines
2.6 KiB

package server
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"github.com/grafana/loki/pkg/util"
"github.com/prometheus/prometheus/promql"
"github.com/weaveworks/common/httpgrpc"
"github.com/weaveworks/common/user"
"github.com/grafana/loki/pkg/logqlmodel"
"github.com/grafana/loki/pkg/storage/chunk"
)
// StatusClientClosedRequest is the status code for when a client request cancellation of an http request
const StatusClientClosedRequest = 499
const (
ErrClientCanceled = "The request was cancelled by the client."
ErrDeadlineExceeded = "Request timed out, decrease the duration of the request or add more label matchers (prefer exact match over regex match) to reduce the amount of data processed."
)
type ErrorResponseBody struct {
Code int `json:"code"`
Status string `json:"status"`
Message string `json:"message"`
}
func NotFoundHandler(w http.ResponseWriter, r *http.Request) {
JSONError(w, 404, "not found")
}
func JSONError(w http.ResponseWriter, code int, message string, args ...interface{}) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(code)
json.NewEncoder(w).Encode(ErrorResponseBody{
Code: code,
Status: "error",
Message: fmt.Sprintf(message, args...),
})
}
// WriteError write a go error with the correct status code.
func WriteError(err error, w http.ResponseWriter) {
var (
queryErr chunk.QueryError
promErr promql.ErrStorage
)
me, ok := err.(util.MultiError)
if ok && me.IsCancel() {
JSONError(w, StatusClientClosedRequest, ErrClientCanceled)
return
}
if ok && me.IsDeadlineExceeded() {
JSONError(w, http.StatusGatewayTimeout, ErrDeadlineExceeded)
return
}
s, isRPC := status.FromError(err)
switch {
case errors.Is(err, context.Canceled) ||
(isRPC && s.Code() == codes.Canceled) ||
(errors.As(err, &promErr) && errors.Is(promErr.Err, context.Canceled)):
JSONError(w, StatusClientClosedRequest, ErrClientCanceled)
case errors.Is(err, context.DeadlineExceeded) ||
(isRPC && s.Code() == codes.DeadlineExceeded):
JSONError(w, http.StatusGatewayTimeout, ErrDeadlineExceeded)
case errors.As(err, &queryErr),
errors.Is(err, logqlmodel.ErrLimit) || errors.Is(err, logqlmodel.ErrParse) || errors.Is(err, logqlmodel.ErrPipeline),
errors.Is(err, user.ErrNoOrgID):
JSONError(w, http.StatusBadRequest, err.Error())
default:
if grpcErr, ok := httpgrpc.HTTPResponseFromError(err); ok {
JSONError(w, int(grpcErr.Code), string(grpcErr.Body))
return
}
JSONError(w, http.StatusInternalServerError, err.Error())
}
}