Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve pipeline stages ast errors. #2805

Merged
merged 1 commit into from
Oct 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pkg/logql/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ func (m MultiStageExpr) stages() ([]log.Stage, error) {
for _, e := range m {
p, err := e.Stage()
if err != nil {
return nil, err
return nil, newStageError(e, err)
}
if p == log.NoopStage {
continue
Expand Down
70 changes: 70 additions & 0 deletions pkg/logql/error.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package logql

import (
"fmt"

"github.com/grafana/loki/pkg/logql/log"
"github.com/prometheus/prometheus/pkg/labels"
)

// ParseError is what is returned when we failed to parse.
type ParseError struct {
msg string
line, col int
}

func (p ParseError) Error() string {
if p.col == 0 && p.line == 0 {
return fmt.Sprintf("parse error : %s", p.msg)
}
return fmt.Sprintf("parse error at line %d, col %d: %s", p.line, p.col, p.msg)
}

func newParseError(msg string, line, col int) ParseError {
return ParseError{
msg: msg,
line: line,
col: col,
}
}

func newStageError(expr Expr, err error) ParseError {
return ParseError{
msg: fmt.Sprintf(`stage '%s' : %s`, expr, err),
line: 0,
col: 0,
}
}

// IsParseError returns true if the err is a ast parsing error.
func IsParseError(err error) bool {
_, ok := err.(ParseError)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could use new errors.Is api :)
https://golang.org/pkg/errors/#Is
Two advantages:

  1. avoid type assertion
  2. can also be wrapped errors!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitively a good idea. I would prefer to keep this for another PR I'm chasing after a bug for 2.0.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

of course :) np!

return ok
}

type pipelineError struct {
metric labels.Labels
errorType string
}

func newPipelineErr(metric labels.Labels) *pipelineError {
return &pipelineError{
metric: metric,
errorType: metric.Get(log.ErrorLabel),
}
}

func (e pipelineError) Error() string {
return fmt.Sprintf(
"pipeline error: '%s' for series: '%s'.\n"+
"Use a label filter to intentionally skip this error. (e.g | __error__!=\"%s\").\n"+
"To skip all potential errors you can match empty errors.(e.g __error__=\"\")\n"+
"The label filter can also be specified after unwrap. (e.g | unwrap latency | __error__=\"\" )\n",
e.errorType, e.metric, e.errorType)
}

// IsPipelineError tells if the error is generated by a Pipeline.
func IsPipelineError(err error) bool {
_, ok := err.(*pipelineError)
return ok
}
27 changes: 0 additions & 27 deletions pkg/logql/evaluator.go
Original file line number Diff line number Diff line change
Expand Up @@ -435,33 +435,6 @@ type rangeVectorEvaluator struct {
err error
}

type pipelineError struct {
metric labels.Labels
errorType string
}

func newPipelineErr(metric labels.Labels) *pipelineError {
return &pipelineError{
metric: metric,
errorType: metric.Get(log.ErrorLabel),
}
}

func (e pipelineError) Error() string {
return fmt.Sprintf(
"pipeline error: '%s' for series: '%s'.\n"+
"Use a label filter to intentionally skip this error. (e.g | __error__!=\"%s\").\n"+
"To skip all potential errors you can match empty errors.(e.g __error__=\"\")\n"+
"The label filter can also be specified after unwrap. (e.g | unwrap latency | __error__=\"\" )\n",
e.errorType, e.metric, e.errorType)
}

// IsPipelineError tells if the error is generated by a Pipeline.
func IsPipelineError(err error) bool {
_, ok := err.(*pipelineError)
return ok
}

func (r *rangeVectorEvaluator) Next() (bool, int64, promql.Vector) {
next := r.iter.Next()
if !next {
Expand Down
28 changes: 0 additions & 28 deletions pkg/logql/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package logql

import (
"errors"
"fmt"
"strings"
"text/scanner"

Expand Down Expand Up @@ -86,30 +85,3 @@ func ParseLogSelector(input string) (LogSelectorExpr, error) {
}
return logSelector, nil
}

// ParseError is what is returned when we failed to parse.
type ParseError struct {
msg string
line, col int
}

func (p ParseError) Error() string {
if p.col == 0 && p.line == 0 {
return fmt.Sprintf("parse error : %s", p.msg)
}
return fmt.Sprintf("parse error at line %d, col %d: %s", p.line, p.col, p.msg)
}

func newParseError(msg string, line, col int) ParseError {
return ParseError{
msg: msg,
line: line,
col: col,
}
}

// IsParseError returns true if the err is a ast parsing error.
func IsParseError(err error) bool {
_, ok := err.(ParseError)
return ok
}