Return 400 instead of 502 when VictoriaLogs rejects a query
A malformed LogsQL query is a user error, not a server failure. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
328384032b
commit
aad0fb7c16
3 files changed
+24
-5
No files matched your search
@@ -75,7 +75,7 @@ func (a *API) logs(w http.ResponseWriter, r *http.Request) {
|
|||||||
start := time.Now()
|
start := time.Now()
|
||||||
rows, err := a.store.Query(r.Context(), q)
|
rows, err := a.store.Query(r.Context(), q)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeErr(w, http.StatusBadGateway, err)
|
writeErr(w, queryStatus(err), err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
a.annotateHosts(rows)
|
a.annotateHosts(rows)
|
||||||
@@ -105,7 +105,7 @@ func (a *API) histogram(w http.ResponseWriter, r *http.Request) {
|
|||||||
q := filter + " | stats by (_time:" + step.expr + ") count() hits | sort by (_time)"
|
q := filter + " | stats by (_time:" + step.expr + ") count() hits | sort by (_time)"
|
||||||
rows, err := a.store.Query(r.Context(), q)
|
rows, err := a.store.Query(r.Context(), q)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeErr(w, http.StatusBadGateway, err)
|
writeErr(w, queryStatus(err), err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
type bucket struct {
|
type bucket struct {
|
||||||
@@ -168,7 +168,7 @@ func (a *API) facets(w http.ResponseWriter, r *http.Request) {
|
|||||||
q := "_time:7d | stats by (" + field + ") count() hits | sort by (hits desc) | limit 300"
|
q := "_time:7d | stats by (" + field + ") count() hits | sort by (hits desc) | limit 300"
|
||||||
rows, err := a.store.Query(r.Context(), q)
|
rows, err := a.store.Query(r.Context(), q)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeErr(w, http.StatusBadGateway, err)
|
writeErr(w, queryStatus(err), err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
vals := make([]string, 0, len(rows))
|
vals := make([]string, 0, len(rows))
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ func (a *API) exportCSV(w http.ResponseWriter, r *http.Request) {
|
|||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if cw == nil {
|
if cw == nil {
|
||||||
writeErr(w, http.StatusBadGateway, err)
|
writeErr(w, queryStatus(err), err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Headers are already sent: the file is truncated, log why.
|
// Headers are already sent: the file is truncated, log why.
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
@@ -170,7 +171,7 @@ func (s *Store) QueryStream(ctx context.Context, query string, fn func(row map[s
|
|||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
msg, _ := io.ReadAll(io.LimitReader(resp.Body, 2048))
|
msg, _ := io.ReadAll(io.LimitReader(resp.Body, 2048))
|
||||||
return fmt.Errorf("%s", strings.TrimSpace(string(msg)))
|
return &queryError{status: resp.StatusCode, msg: strings.TrimSpace(string(msg))}
|
||||||
}
|
}
|
||||||
|
|
||||||
sc := bufio.NewScanner(resp.Body)
|
sc := bufio.NewScanner(resp.Body)
|
||||||
@@ -245,6 +246,24 @@ func (s *Store) deleteCall(ctx context.Context, path string) ([]byte, error) {
|
|||||||
return body, nil
|
return body, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// queryError is an error answer of VictoriaLogs to a query.
|
||||||
|
type queryError struct {
|
||||||
|
status int
|
||||||
|
msg string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *queryError) Error() string { return e.msg }
|
||||||
|
|
||||||
|
// queryStatus is the HTTP status to return for a failed query: 400 when
|
||||||
|
// VictoriaLogs rejected the query itself (bad LogsQL), 502 otherwise.
|
||||||
|
func queryStatus(err error) int {
|
||||||
|
var qe *queryError
|
||||||
|
if errors.As(err, &qe) && qe.status >= 400 && qe.status < 500 {
|
||||||
|
return http.StatusBadRequest
|
||||||
|
}
|
||||||
|
return http.StatusBadGateway
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Store) Stats() map[string]any {
|
func (s *Store) Stats() map[string]any {
|
||||||
return map[string]any{
|
return map[string]any{
|
||||||
"received": s.received.Load(),
|
"received": s.received.Load(),
|
||||||
|
|||||||
Reference in new issue
Block a user