lib/logstorage: allow special chars in unquoted _stream tag names and values

This simplifies writing _stream filters. For example,

{foo-bar=abc:de}

can be written instead of

{"foo-bar"="abc:de"}
This commit is contained in:
Aliaksandr Valialkin 2024-10-07 15:08:46 +02:00
parent 462b7cd597
commit 89686094a0
No known key found for this signature in database
GPG key ID: 52C003EE2BCDB9EB
3 changed files with 29 additions and 11 deletions

View file

@ -919,6 +919,10 @@ func getCompoundSuffix(lex *lexer, allowColon bool) string {
func getCompoundToken(lex *lexer) (string, error) { func getCompoundToken(lex *lexer) (string, error) {
stopTokens := []string{",", "(", ")", "[", "]", "|", ""} stopTokens := []string{",", "(", ")", "[", "]", "|", ""}
return getCompoundTokenExt(lex, stopTokens)
}
func getCompoundTokenExt(lex *lexer, stopTokens []string) (string, error) {
if lex.isKeyword(stopTokens...) { if lex.isKeyword(stopTokens...) {
return "", fmt.Errorf("compound token cannot start with '%s'", lex.token) return "", fmt.Errorf("compound token cannot start with '%s'", lex.token)
} }

View file

@ -86,7 +86,8 @@ func (af *andStreamFilter) String() string {
type streamTagFilter struct { type streamTagFilter struct {
// tagName is the name for the tag to filter // tagName is the name for the tag to filter
tagName string tagName string
// op is operation such as `=`, `!=`, `=~` or `!~`
// op is operation such as `=`, `!=`, `=~`, `!~` or `:`
op string op string
// value is the value // value is the value
@ -164,20 +165,23 @@ func parseAndStreamFilter(lex *lexer) (*andStreamFilter, error) {
} }
func parseStreamTagFilter(lex *lexer) (*streamTagFilter, error) { func parseStreamTagFilter(lex *lexer) (*streamTagFilter, error) {
tagName := lex.token // parse tagName
if !lex.mustNextToken() { tagName, err := parseStreamTagName(lex)
return nil, fmt.Errorf("missing operation in _stream filter for %q field", tagName) if err != nil {
return nil, fmt.Errorf("cannot parse stream tag name: %w", err)
} }
if !lex.isKeyword("=", "!=", "=~", "!~") { if !lex.isKeyword("=", "!=", "=~", "!~") {
return nil, fmt.Errorf("unsupported operation %q in _steam filter for %q field; supported operations: =, !=, =~, !~", lex.token, tagName) return nil, fmt.Errorf("unsupported operation %q in _steam filter for %q field; supported operations: =, !=, =~, !~", lex.token, tagName)
} }
// parse op
op := lex.token op := lex.token
if !lex.mustNextToken() { lex.nextToken()
return nil, fmt.Errorf("missing _stream filter value for %q field", tagName)
} // parse tag value
value := lex.token value, err := parseStreamTagValue(lex)
if !lex.mustNextToken() { if err != nil {
return nil, fmt.Errorf("missing token after %q%s%q filter", tagName, op, value) return nil, fmt.Errorf("cannot parse value for tag %q: %w", tagName, err)
} }
stf := &streamTagFilter{ stf := &streamTagFilter{
tagName: tagName, tagName: tagName,
@ -194,6 +198,16 @@ func parseStreamTagFilter(lex *lexer) (*streamTagFilter, error) {
return stf, nil return stf, nil
} }
func parseStreamTagName(lex *lexer) (string, error) {
stopTokens := []string{"=", "!=", "=~", "!~", ",", "{", "}", "'", `"`, "`", ""}
return getCompoundTokenExt(lex, stopTokens)
}
func parseStreamTagValue(lex *lexer) (string, error) {
stopTokens := []string{",", "{", "}", "'", `"`, "`", ""}
return getCompoundTokenExt(lex, stopTokens)
}
func getStreamName() *streamName { func getStreamName() *streamName {
v := streamNamePool.Get() v := streamNamePool.Get()
if v == nil { if v == nil {

View file

@ -163,7 +163,7 @@ func TestNewTestStreamFilterSuccess(t *testing.T) {
f(`{foo="bar"}`, `{foo="bar"}`) f(`{foo="bar"}`, `{foo="bar"}`)
f(`{ "foo" =~ "bar.+" , baz!="a" or x="y"}`, `{foo=~"bar.+",baz!="a" or x="y"}`) f(`{ "foo" =~ "bar.+" , baz!="a" or x="y"}`, `{foo=~"bar.+",baz!="a" or x="y"}`)
f(`{"a b"='c}"d' OR de="aaa"}`, `{"a b"="c}\"d" or de="aaa"}`) f(`{"a b"='c}"d' OR de="aaa"}`, `{"a b"="c}\"d" or de="aaa"}`)
f(`{a="b", c="d" or x="y"}`, `{a="b",c="d" or x="y"}`) f(`{a-q:w.z="b", c="d" or 'x a'=y-z=q}`, `{"a-q:w.z"="b",c="d" or "x a"="y-z=q"}`)
} }
func TestNewTestStreamFilterFailure(t *testing.T) { func TestNewTestStreamFilterFailure(t *testing.T) {