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/ruler/base/error_translate_queryable.go

134 lines
3.9 KiB

package base
import (
"context"
"github.com/gogo/status"
"github.com/pkg/errors"
"github.com/prometheus/prometheus/model/labels"
"github.com/prometheus/prometheus/promql"
"github.com/prometheus/prometheus/storage"
storage_errors "github.com/grafana/loki/pkg/storage/errors"
"github.com/grafana/loki/pkg/util/validation"
)
// TranslateToPromqlAPIError converts error to one of promql.Errors for consumption in PromQL API.
// PromQL API only recognizes few errors, and converts everything else to HTTP status code 422.
//
// Specifically, it supports:
//
// promql.ErrQueryCanceled, mapped to 503
// promql.ErrQueryTimeout, mapped to 503
// promql.ErrStorage mapped to 500
// anything else is mapped to 422
//
// Querier code produces different kinds of errors, and we want to map them to above-mentioned HTTP status codes correctly.
//
// Details:
// - vendor/github.com/prometheus/prometheus/web/api/v1/api.go, respondError function only accepts *apiError types.
// - translation of error to *apiError happens in vendor/github.com/prometheus/prometheus/web/api/v1/api.go, returnAPIError method.
func TranslateToPromqlAPIError(err error) error {
if err == nil {
return err
}
switch errors.Cause(err).(type) {
case promql.ErrStorage, promql.ErrTooManySamples, promql.ErrQueryCanceled, promql.ErrQueryTimeout:
// Don't translate those, just in case we use them internally.
return err
case storage_errors.QueryError, validation.LimitError:
// This will be returned with status code 422 by Prometheus API.
return err
default:
if errors.Is(err, context.Canceled) {
return err // 422
}
s, ok := status.FromError(err)
if !ok {
s, ok = status.FromError(errors.Cause(err))
}
if ok {
code := s.Code()
// Treat these as HTTP status codes, even though they are supposed to be grpc codes.
if code >= 400 && code < 500 {
// Return directly, will be mapped to 422
return err
} else if code >= 500 && code < 599 {
// Wrap into ErrStorage for mapping to 500
return promql.ErrStorage{Err: err}
}
}
// All other errors will be returned as 500.
return promql.ErrStorage{Err: err}
}
}
// ErrTranslateFn is used to translate or wrap error before returning it by functions in
// storage.SampleAndChunkQueryable interface.
// Input error may be nil.
type ErrTranslateFn func(err error) error
func NewErrorTranslateQueryableWithFn(q storage.Queryable, fn ErrTranslateFn) storage.Queryable {
return errorTranslateQueryable{q: q, fn: fn}
}
type errorTranslateQueryable struct {
q storage.Queryable
fn ErrTranslateFn
}
func (e errorTranslateQueryable) Querier(ctx context.Context, mint, maxt int64) (storage.Querier, error) {
q, err := e.q.Querier(ctx, mint, maxt)
return errorTranslateQuerier{q: q, fn: e.fn}, e.fn(err)
}
type errorTranslateQuerier struct {
q storage.Querier
fn ErrTranslateFn
}
func (e errorTranslateQuerier) LabelValues(name string, matchers ...*labels.Matcher) ([]string, storage.Warnings, error) {
values, warnings, err := e.q.LabelValues(name, matchers...)
return values, warnings, e.fn(err)
}
func (e errorTranslateQuerier) LabelNames(matchers ...*labels.Matcher) ([]string, storage.Warnings, error) {
values, warnings, err := e.q.LabelNames(matchers...)
return values, warnings, e.fn(err)
}
func (e errorTranslateQuerier) Close() error {
return e.fn(e.q.Close())
}
func (e errorTranslateQuerier) Select(sortSeries bool, hints *storage.SelectHints, matchers ...*labels.Matcher) storage.SeriesSet {
s := e.q.Select(sortSeries, hints, matchers...)
return errorTranslateSeriesSet{s: s, fn: e.fn}
}
type errorTranslateSeriesSet struct {
s storage.SeriesSet
fn ErrTranslateFn
}
func (e errorTranslateSeriesSet) Next() bool {
return e.s.Next()
}
func (e errorTranslateSeriesSet) At() storage.Series {
return e.s.At()
}
func (e errorTranslateSeriesSet) Err() error {
return e.fn(e.s.Err())
}
func (e errorTranslateSeriesSet) Warnings() storage.Warnings {
return e.s.Warnings()
}