diff --git a/promql/parse.go b/promql/parse.go index 237670323..a51e0c550 100644 --- a/promql/parse.go +++ b/promql/parse.go @@ -20,7 +20,10 @@ import ( "strings" "time" + "github.com/prometheus/log" + clientmodel "github.com/prometheus/client_golang/model" + "github.com/prometheus/prometheus/storage/metric" "github.com/prometheus/prometheus/util/strutil" ) @@ -323,9 +326,15 @@ func (p *parser) recover(errp *error) { e := recover() if e != nil { if _, ok := e.(runtime.Error); ok { - panic(e) + // Print the stack trace but do not inhibit the running application. + buf := make([]byte, 64<<10) + buf = buf[:runtime.Stack(buf, false)] + + log.Errorf("parser panic: %v\n%s", e, buf) + *errp = fmt.Errorf("unexpected error") + } else { + *errp = e.(error) } - *errp = e.(error) } return } diff --git a/promql/parse_test.go b/promql/parse_test.go index b46dc4a1c..341fe8e99 100644 --- a/promql/parse_test.go +++ b/promql/parse_test.go @@ -1354,3 +1354,30 @@ func TestParseSeries(t *testing.T) { } } } + +func TestRecoverRuntime(t *testing.T) { + var p *parser + var err error + defer p.recover(&err) + + // Cause a runtime panic. + var a []int + a[123] = 1 + + if err.Error() != "unexpected error" { + t.Fatalf("wrong error message: %q, expected %q", err, "unexpected error") + } +} + +func TestRecoverError(t *testing.T) { + var p *parser + var err error + defer p.recover(&err) + + e := fmt.Errorf("custom error") + panic(e) + + if err.Error() != e.Error() { + t.Fatalf("wrong error message: %q, expected %q", err, e) + } +}