Skip to content

Commit e8a7e05

Browse files
bencliveshantanualsi
authored andcommitted
fix: Ensure Drain patterns are valid for LogQL pattern match filter (#12815)
1 parent c97d6d0 commit e8a7e05

File tree

2 files changed

+118
-1
lines changed

2 files changed

+118
-1
lines changed

pkg/pattern/drain/drain.go

-1
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,6 @@ func (d *Drain) Match(content string) *LogCluster {
268268
}
269269

270270
func (d *Drain) getContentAsTokens(content string) []string {
271-
content = strings.TrimSpace(content)
272271
for _, extraDelimiter := range d.config.ExtraDelimiters {
273272
content = strings.Replace(content, extraDelimiter, " ", -1)
274273
}

pkg/pattern/drain/drain_test.go

+118
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,12 @@ import (
66
"testing"
77

88
"github.com/stretchr/testify/require"
9+
10+
"github.com/grafana/loki/v3/pkg/logql/log/pattern"
911
)
1012

1113
func TestDrain_TrainExtractsPatterns(t *testing.T) {
14+
t.Parallel()
1215
tests := []struct {
1316
name string
1417
drain *Drain
@@ -116,3 +119,118 @@ func TestDrain_TrainExtractsPatterns(t *testing.T) {
116119
})
117120
}
118121
}
122+
123+
func TestDrain_TrainGeneratesMatchablePatterns(t *testing.T) {
124+
t.Parallel()
125+
tests := []struct {
126+
name string
127+
drain *Drain
128+
inputLines []string
129+
}{
130+
{
131+
name: "should match each line against a pattern",
132+
drain: New(DefaultConfig()),
133+
inputLines: []string{
134+
`test test test`,
135+
`test test test`,
136+
`test test test`,
137+
`test test test`,
138+
},
139+
},
140+
{
141+
name: "should also match newlines",
142+
drain: New(DefaultConfig()),
143+
inputLines: []string{
144+
"test test test\n",
145+
"test test test\n",
146+
"test test test\n",
147+
"test test test\n",
148+
},
149+
},
150+
}
151+
for _, tt := range tests {
152+
tt := tt
153+
t.Run(tt.name, func(t *testing.T) {
154+
for _, line := range tt.inputLines {
155+
tt.drain.Train(line, 0)
156+
}
157+
t.Log("Learned clusters", tt.drain.Clusters())
158+
159+
for _, line := range tt.inputLines {
160+
match := tt.drain.Match(line)
161+
require.NotNil(t, match, "Line should match a cluster")
162+
}
163+
})
164+
}
165+
166+
}
167+
168+
func TestDrain_TrainGeneratesPatternsMatchableByLokiPatternFilter(t *testing.T) {
169+
t.Parallel()
170+
tests := []struct {
171+
name string
172+
drain *Drain
173+
inputLines []string
174+
}{
175+
{
176+
name: "should extract patterns that all lines match",
177+
drain: New(DefaultConfig()),
178+
inputLines: []string{
179+
`test 1 test`,
180+
`test 2 test`,
181+
`test 3 test`,
182+
`test 4 test`,
183+
},
184+
},
185+
{
186+
name: "should extract patterns that match if line ends with newlines",
187+
drain: New(DefaultConfig()),
188+
inputLines: []string{
189+
"test 1 test\n",
190+
"test 2 test\n",
191+
"test 3 test\n",
192+
"test 4 test\n",
193+
},
194+
},
195+
{
196+
name: "should extract patterns that match if line ends with empty space",
197+
drain: New(DefaultConfig()),
198+
inputLines: []string{
199+
"test 1 test ",
200+
"test 2 test ",
201+
"test 3 test ",
202+
"test 4 test ",
203+
},
204+
},
205+
{
206+
name: "should extract patterns that match if line starts with empty space",
207+
drain: New(DefaultConfig()),
208+
inputLines: []string{
209+
" test 1 test",
210+
" test 2 test",
211+
" test 3 test",
212+
" test 4 test",
213+
},
214+
},
215+
}
216+
for _, tt := range tests {
217+
tt := tt
218+
t.Run(tt.name, func(t *testing.T) {
219+
for _, line := range tt.inputLines {
220+
tt.drain.Train(line, 0)
221+
}
222+
require.Equal(t, 1, len(tt.drain.Clusters()))
223+
cluster := tt.drain.Clusters()[0]
224+
t.Log("Extracted cluster: ", cluster)
225+
226+
matcher, err := pattern.ParseLineFilter([]byte(cluster.String()))
227+
require.NoError(t, err)
228+
229+
for _, line := range tt.inputLines {
230+
passes := matcher.Test([]byte(line))
231+
require.Truef(t, passes, "Line %q should match extracted pattern", line)
232+
}
233+
})
234+
}
235+
236+
}

0 commit comments

Comments
 (0)