Skip to content

Commit

Permalink
refactor(logql): compile pattern during parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
tdakkota committed Jul 8, 2024
1 parent 0591eaa commit 9f61d6f
Show file tree
Hide file tree
Showing 7 changed files with 43 additions and 27 deletions.
11 changes: 10 additions & 1 deletion internal/logql/logqlengine/logqlpattern/logqlpattern.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,16 @@ const (
LineFilterFlags = DisallowNamed
)

// Parse parses pattern.
// MustParse is like [Parse] but panics if the expression cannot be parsed.
func MustParse(input string, flags ParseFlags) Pattern {
p, err := Parse(input, flags)
if err != nil {
panic(err)
}
return p
}

// Parse parses LogQL pattern.
func Parse(input string, flags ParseFlags) (p Pattern, _ error) {
if !utf8.ValidString(input) {
return p, errors.New("pattern is invalid UTF-8")
Expand Down
10 changes: 3 additions & 7 deletions internal/logql/logqlengine/logqlpattern/match.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
package logqlpattern

import (
"strings"

"github.com/go-faster/oteldb/internal/logql"
)
import "strings"

// Match matches given pattern against input string.
func Match(p Pattern, input string, match func(label logql.Label, value string)) bool {
func Match[Label ~string](p Pattern, input string, match func(label Label, value string)) bool {
parts := p.Parts
if len(parts) == 0 && input == "" {
return true
Expand All @@ -24,7 +20,7 @@ func Match(p Pattern, input string, match func(label logql.Label, value string))
}
case Capture:
var (
label = logql.Label(part.Value)
label = Label(part.Value)
value string
)
if i+1 < len(parts) {
Expand Down
8 changes: 3 additions & 5 deletions internal/logql/logqlengine/logqlpattern/match_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import (
"testing"

"github.com/stretchr/testify/require"

"github.com/go-faster/oteldb/internal/logql"
)

var matchTests = []struct {
Expand Down Expand Up @@ -145,8 +143,8 @@ func TestMatch(t *testing.T) {
require.NoError(t, err)

matches := map[string]string{}
fullMatch := Match(compiled, tt.input, func(label logql.Label, value string) {
matches[string(label)] = value
fullMatch := Match(compiled, tt.input, func(label, value string) {
matches[label] = value
})
require.Equal(t, tt.match, matches)
require.Equal(t, tt.full, fullMatch)
Expand All @@ -164,6 +162,6 @@ func FuzzMatch(f *testing.F) {
t.Skipf("Invalid pattern %q: %+v", pattern, err)
return
}
Match(compiled, input, func(logql.Label, string) {})
Match(compiled, input, func(string, string) {})
})
}
10 changes: 1 addition & 9 deletions internal/logql/logqlengine/pattern.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package logqlengine

import (
"github.com/go-faster/errors"
"go.opentelemetry.io/collector/pdata/pcommon"

"github.com/go-faster/oteldb/internal/logql"
Expand All @@ -16,14 +15,7 @@ type PatternExtractor struct {
}

func buildPatternExtractor(stage *logql.PatternLabelParser) (Processor, error) {
compiled, err := logqlpattern.Parse(stage.Pattern, logqlpattern.ExtractorFlags)
if err != nil {
return nil, errors.Wrapf(err, "parse pattern %q", stage.Pattern)
}

return &PatternExtractor{
pattern: compiled,
}, nil
return &PatternExtractor{pattern: stage.Pattern}, nil
}

// Process implements Processor.
Expand Down
13 changes: 10 additions & 3 deletions internal/logql/parser_pipeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"github.com/go-faster/errors"

"github.com/go-faster/oteldb/internal/logql/lexer"
"github.com/go-faster/oteldb/internal/logql/logqlengine/logqlpattern"
)

func (p *parser) parsePipeline(allowUnwrap bool) (stages []PipelineStage, err error) {
Expand Down Expand Up @@ -49,12 +50,18 @@ func (p *parser) parsePipeline(allowUnwrap bool) (stages []PipelineStage, err er
}
stages = append(stages, p)
case lexer.Pattern:
pattern, err := p.parseString()
pattern, patternTok, err := p.consumeText(lexer.String)
if err != nil {
return stages, err
}
// FIXME(tdakkota): parse pattern?
stages = append(stages, &PatternLabelParser{Pattern: pattern})
compiled, err := logqlpattern.Parse(pattern, logqlpattern.ExtractorFlags)
if err != nil {
return nil, &ParseError{
Pos: patternTok.Pos,
Err: errors.Wrap(err, "pattern"),
}
}
stages = append(stages, &PatternLabelParser{Pattern: compiled})
case lexer.Unpack:
stages = append(stages, &UnpackLabelParser{})
case lexer.LineFormat:
Expand Down
14 changes: 13 additions & 1 deletion internal/logql/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"time"

"github.com/stretchr/testify/require"

"github.com/go-faster/oteldb/internal/logql/logqlengine/logqlpattern"
)

func ptrTo[T any](v T) *T {
Expand Down Expand Up @@ -226,7 +228,7 @@ var tests = []TestCase{
1: "method",
},
},
&PatternLabelParser{Pattern: "<ip>"},
&PatternLabelParser{Pattern: logqlpattern.MustParse("<ip>", logqlpattern.ExtractorFlags)},
&UnpackLabelParser{},
&LineFormat{Template: "{{ . }}"},
&DecolorizeExpr{},
Expand Down Expand Up @@ -1177,6 +1179,7 @@ var tests = []TestCase{
{`{foo = "bar"} | label_format status=foo,status=bar`, nil, true},

// Invalid regexp.
//
{`{foo=~"\\"}`, nil, true},
{`{} |~ "\\"`, nil, true},
{`{} |~ ".+" or "\\"`, nil, true},
Expand All @@ -1187,6 +1190,15 @@ var tests = []TestCase{
{`{} | regexp "(?P<method>\\w+)(?P<method>\\w+)"`, nil, true},
// Invalid capture name.
{`{} | regexp "(?P<0a>\\w+)"`, nil, true},

// Invalid pattern.
//
// No capture.
{`{} | pattern "a"`, nil, true},
// Duplicate capture.
{`{} | pattern "<a> foo <a>"`, nil, true},
// Consecutive capture.
{`{} | pattern "<a><b>"`, nil, true},
}

func TestParse(t *testing.T) {
Expand Down
4 changes: 3 additions & 1 deletion internal/logql/pipeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"time"

"github.com/dustin/go-humanize"

"github.com/go-faster/oteldb/internal/logql/logqlengine/logqlpattern"
)

// PipelineStage is a LogQL pipeline stage.
Expand Down Expand Up @@ -144,7 +146,7 @@ type RegexpLabelParser struct {
//
// See https://grafana.com/docs/loki/latest/logql/log_queries/#pattern.
type PatternLabelParser struct {
Pattern string
Pattern logqlpattern.Pattern
}

// UnpackLabelParser unpacks data from promtail.
Expand Down

0 comments on commit 9f61d6f

Please sign in to comment.