fix(parsers.xpath): Ensure precedence of explicitly defined tags and fields (#13662)
This commit is contained in:
parent
64f6c97d13
commit
da28cfdb43
|
|
@ -280,7 +280,8 @@ in the metric.
|
|||
__Please note__: The resulting fields are _always_ of type string!
|
||||
|
||||
It is also possible to specify a mixture of the two alternative ways of
|
||||
specifying fields.
|
||||
specifying fields. In this case _explicitly_ defined tags and fields take
|
||||
_precedence_ over the batch instances if both use the same tag/field name.
|
||||
|
||||
### metric_selection (optional)
|
||||
|
||||
|
|
|
|||
|
|
@ -279,25 +279,6 @@ func (p *Parser) parseQuery(starttime time.Time, doc, selected dataNode, config
|
|||
|
||||
// Query tags and add default ones
|
||||
tags := make(map[string]string)
|
||||
for name, query := range config.Tags {
|
||||
// Execute the query and cast the returned values into strings
|
||||
v, err := p.executeQuery(doc, selected, query)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to query tag %q: %w", name, err)
|
||||
}
|
||||
switch v := v.(type) {
|
||||
case string:
|
||||
tags[name] = v
|
||||
case bool:
|
||||
tags[name] = strconv.FormatBool(v)
|
||||
case float64:
|
||||
tags[name] = strconv.FormatFloat(v, 'G', -1, 64)
|
||||
case nil:
|
||||
continue
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown format '%T' for tag %q", v, name)
|
||||
}
|
||||
}
|
||||
|
||||
// Handle the tag batch definitions if any.
|
||||
if len(config.TagSelection) > 0 {
|
||||
|
|
@ -356,53 +337,34 @@ func (p *Parser) parseQuery(starttime time.Time, doc, selected dataNode, config
|
|||
}
|
||||
}
|
||||
|
||||
// Handle explicitly defined tags
|
||||
for name, query := range config.Tags {
|
||||
// Execute the query and cast the returned values into strings
|
||||
v, err := p.executeQuery(doc, selected, query)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to query tag %q: %w", name, err)
|
||||
}
|
||||
switch v := v.(type) {
|
||||
case string:
|
||||
tags[name] = v
|
||||
case bool:
|
||||
tags[name] = strconv.FormatBool(v)
|
||||
case float64:
|
||||
tags[name] = strconv.FormatFloat(v, 'G', -1, 64)
|
||||
case nil:
|
||||
continue
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown format '%T' for tag %q", v, name)
|
||||
}
|
||||
}
|
||||
|
||||
// Add default tags
|
||||
for name, v := range p.DefaultTags {
|
||||
tags[name] = v
|
||||
}
|
||||
|
||||
// Query fields
|
||||
fields := make(map[string]interface{})
|
||||
for name, query := range config.FieldsInt {
|
||||
// Execute the query and cast the returned values into integers
|
||||
v, err := p.executeQuery(doc, selected, query)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to query field (int) %q: %w", name, err)
|
||||
}
|
||||
switch v := v.(type) {
|
||||
case string:
|
||||
fields[name], err = strconv.ParseInt(v, 10, 54)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse field (int) %q: %w", name, err)
|
||||
}
|
||||
case bool:
|
||||
fields[name] = int64(0)
|
||||
if v {
|
||||
fields[name] = int64(1)
|
||||
}
|
||||
case float64:
|
||||
fields[name] = int64(v)
|
||||
case nil:
|
||||
continue
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown format '%T' for field (int) %q", v, name)
|
||||
}
|
||||
}
|
||||
|
||||
for name, query := range config.Fields {
|
||||
// Execute the query and store the result in fields
|
||||
v, err := p.executeQuery(doc, selected, query)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to query field %q: %w", name, err)
|
||||
}
|
||||
|
||||
if config.FieldsHexFilter != nil && config.FieldsHexFilter.Match(name) {
|
||||
if b, ok := v.([]byte); ok {
|
||||
v = hex.EncodeToString(b)
|
||||
}
|
||||
}
|
||||
|
||||
fields[name] = v
|
||||
}
|
||||
|
||||
// Handle the field batch definitions if any.
|
||||
if len(config.FieldSelection) > 0 {
|
||||
|
|
@ -471,6 +433,59 @@ func (p *Parser) parseQuery(starttime time.Time, doc, selected dataNode, config
|
|||
}
|
||||
}
|
||||
|
||||
// Handle explicitly defined fields
|
||||
for name, query := range config.FieldsInt {
|
||||
// Execute the query and cast the returned values into integers
|
||||
v, err := p.executeQuery(doc, selected, query)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to query field (int) %q: %w", name, err)
|
||||
}
|
||||
switch v := v.(type) {
|
||||
case string:
|
||||
fields[name], err = strconv.ParseInt(v, 10, 54)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse field (int) %q: %w", name, err)
|
||||
}
|
||||
case bool:
|
||||
fields[name] = int64(0)
|
||||
if v {
|
||||
fields[name] = int64(1)
|
||||
}
|
||||
case float64:
|
||||
fields[name] = int64(v)
|
||||
case nil:
|
||||
continue
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown format '%T' for field (int) %q", v, name)
|
||||
}
|
||||
}
|
||||
|
||||
for name, query := range config.Fields {
|
||||
// Execute the query and store the result in fields
|
||||
v, err := p.executeQuery(doc, selected, query)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to query field %q: %w", name, err)
|
||||
}
|
||||
|
||||
// Handle complex types which would be dropped otherwise for
|
||||
// native type handling
|
||||
fmt.Printf("explicit field %q: %v (%T)\n", name, v, v)
|
||||
if v != nil {
|
||||
switch reflect.TypeOf(v).Kind() {
|
||||
case reflect.Array, reflect.Slice, reflect.Map:
|
||||
if b, ok := v.([]byte); ok {
|
||||
if config.FieldsHexFilter != nil && config.FieldsHexFilter.Match(name) {
|
||||
v = hex.EncodeToString(b)
|
||||
}
|
||||
} else {
|
||||
v = fmt.Sprintf("%v", v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fields[name] = v
|
||||
}
|
||||
|
||||
return metric.New(metricname, tags, fields, timestamp), nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
foo a="a string",b=3.1415,c=true,d="{\"d1\":1,\"d2\":\"foo\",\"d3\":true,\"d4\":null}",e="[\"master\",42,true]",timestamp=1690193829 1690193829000000000
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
[[inputs.file]]
|
||||
files = ["./testcases/json_string_representation/test.json"]
|
||||
data_format = "xpath_json"
|
||||
|
||||
xpath_native_types = true
|
||||
|
||||
[[inputs.file.xpath]]
|
||||
metric_name = "'foo'"
|
||||
field_selection = "*"
|
||||
timestamp = "timestamp"
|
||||
timestamp_format = "unix"
|
||||
|
||||
[inputs.file.xpath.fields]
|
||||
d = "string(d)"
|
||||
e = "string(e)"
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"a": "a string",
|
||||
"b": 3.1415,
|
||||
"c": true,
|
||||
"d": {
|
||||
"d1": 1,
|
||||
"d2": "foo",
|
||||
"d3": true,
|
||||
"d4": null
|
||||
},
|
||||
"e": ["master", 42, true],
|
||||
"timestamp": 1690193829
|
||||
}
|
||||
Loading…
Reference in New Issue