Skip to content

Commit d216856

Browse files
committed
Add jsonpath support
Signed-off-by: Christian Haudum <christian.haudum@gmail.com>
1 parent 167f350 commit d216856

File tree

3 files changed

+36
-14
lines changed

3 files changed

+36
-14
lines changed

‎pkg/distributor/field_detection.go

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import (
1313

1414
"github.com/grafana/loki/v3/pkg/loghttp/push"
1515
"github.com/grafana/loki/v3/pkg/logproto"
16+
"github.com/grafana/loki/v3/pkg/logql/log"
17+
"github.com/grafana/loki/v3/pkg/logql/log/jsonexpr"
1618
"github.com/grafana/loki/v3/pkg/logql/log/logfmt"
1719
"github.com/grafana/loki/v3/pkg/util/constants"
1820
)
@@ -141,9 +143,9 @@ func (l *FieldDetector) detectGenericFieldFromLogEntry(entry logproto.Entry, hin
141143
lineBytes := unsafe.Slice(unsafe.StringData(entry.Line), len(entry.Line))
142144
var v []byte
143145
if isJSON(entry.Line) {
144-
v = l.getValueUsingJSONParser(lineBytes, hints)
146+
v = getValueUsingJSONParser(lineBytes, hints)
145147
} else if isLogFmt(lineBytes) {
146-
v = l.getValueUsingLogfmtParser(lineBytes, hints)
148+
v = getValueUsingLogfmtParser(lineBytes, hints)
147149
}
148150
return string(v)
149151
}
@@ -152,9 +154,9 @@ func (l *FieldDetector) extractLogLevelFromLogLine(log string) string {
152154
lineBytes := unsafe.Slice(unsafe.StringData(log), len(log))
153155
var v []byte
154156
if isJSON(log) {
155-
v = l.getValueUsingJSONParser(lineBytes, l.allowedLevelLabels)
157+
v = getValueUsingJSONParser(lineBytes, l.allowedLevelLabels)
156158
} else if isLogFmt(lineBytes) {
157-
v = l.getValueUsingLogfmtParser(lineBytes, l.allowedLevelLabels)
159+
v = getValueUsingLogfmtParser(lineBytes, l.allowedLevelLabels)
158160
} else {
159161
return detectLevelFromLogLine(log)
160162
}
@@ -179,7 +181,7 @@ func (l *FieldDetector) extractLogLevelFromLogLine(log string) string {
179181
}
180182
}
181183

182-
func (l *FieldDetector) getValueUsingLogfmtParser(line []byte, hints []string) []byte {
184+
func getValueUsingLogfmtParser(line []byte, hints []string) []byte {
183185
d := logfmt.NewDecoder(line)
184186
// In order to have the same behaviour as the JSON field extraction,
185187
// the full line needs to be parsed to extract all possible matching fields.
@@ -201,14 +203,20 @@ func (l *FieldDetector) getValueUsingLogfmtParser(line []byte, hints []string) [
201203
return res
202204
}
203205

204-
func (l *FieldDetector) getValueUsingJSONParser(log []byte, hints []string) []byte {
206+
func getValueUsingJSONParser(line []byte, hints []string) []byte {
207+
var res []byte
205208
for _, allowedLabel := range hints {
206-
l, _, _, err := jsonparser.Get(log, allowedLabel)
207-
if err == nil {
208-
return l
209+
parsed, err := jsonexpr.Parse(allowedLabel, false)
210+
if err != nil {
211+
continue
209212
}
213+
l, _, _, err := jsonparser.Get(line, log.JSONPathsToStrings(parsed)...)
214+
if err != nil {
215+
continue
216+
}
217+
return l
210218
}
211-
return nil
219+
return res
212220
}
213221

214222
func isLogFmt(line []byte) bool {

‎pkg/distributor/field_detection_test.go

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -468,8 +468,9 @@ func Test_DetectGenericFields(t *testing.T) {
468468
detector := newFieldDetector(
469469
validationContext{
470470
discoverGenericFields: map[string][]string{
471-
"trace_id": []string{"trace_id"},
472-
"org_id": []string{"org_id", "user_id", "tenant_id"},
471+
"trace_id": []string{"trace_id"},
472+
"org_id": []string{"org_id", "user_id", "tenant_id"},
473+
"product_id": []string{"product.id"}, // jsonpath
473474
},
474475
allowStructuredMetadata: true,
475476
})
@@ -577,6 +578,19 @@ func Test_DetectGenericFields(t *testing.T) {
577578
{Name: "org_id", Value: "fake_b"}, // first field from configuration that matches takes precedence
578579
},
579580
},
581+
{
582+
name: "logline matches jsonpath",
583+
labels: labels.Labels{
584+
{Name: "env", Value: "prod"},
585+
},
586+
entry: push.Entry{
587+
Line: `{"product": {"details": "product details", "id": "P2024/01"}}`,
588+
StructuredMetadata: push.LabelsAdapter{},
589+
},
590+
expected: push.LabelsAdapter{
591+
{Name: "product_id", Value: "P2024/01"},
592+
},
593+
},
580594
} {
581595
t.Run(tc.name, func(t *testing.T) {
582596
extracted := push.LabelsAdapter{}

‎pkg/logql/log/parser.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -571,7 +571,7 @@ func NewJSONExpressionParser(expressions []LabelExtractionExpr) (*JSONExpression
571571
}
572572

573573
ids = append(ids, exp.Identifier)
574-
paths = append(paths, pathsToString(path))
574+
paths = append(paths, JSONPathsToStrings(path))
575575
}
576576

577577
return &JSONExpressionParser{
@@ -581,7 +581,7 @@ func NewJSONExpressionParser(expressions []LabelExtractionExpr) (*JSONExpression
581581
}, nil
582582
}
583583

584-
func pathsToString(paths []interface{}) []string {
584+
func JSONPathsToStrings(paths []interface{}) []string {
585585
stingPaths := make([]string, 0, len(paths))
586586
for _, p := range paths {
587587
switch v := p.(type) {

0 commit comments

Comments
 (0)