Add strict mode to JSON parser (#6536)
This commit is contained in:
parent
41d6a1a787
commit
a9a0d4048a
|
@ -18,6 +18,10 @@ ignored unless specified in the `tag_key` or `json_string_fields` options.
|
||||||
## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_INPUT.md
|
## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_INPUT.md
|
||||||
data_format = "json"
|
data_format = "json"
|
||||||
|
|
||||||
|
## When strict is true and a JSON array is being parsed, all objects within the
|
||||||
|
## array must be valid
|
||||||
|
strict = false
|
||||||
|
|
||||||
## Query is a GJSON path that specifies a specific chunk of JSON to be
|
## Query is a GJSON path that specifies a specific chunk of JSON to be
|
||||||
## parsed, if not specified the whole document will be parsed.
|
## parsed, if not specified the whole document will be parsed.
|
||||||
##
|
##
|
||||||
|
|
|
@ -32,6 +32,7 @@ type Config struct {
|
||||||
TimeFormat string
|
TimeFormat string
|
||||||
Timezone string
|
Timezone string
|
||||||
DefaultTags map[string]string
|
DefaultTags map[string]string
|
||||||
|
Strict bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type Parser struct {
|
type Parser struct {
|
||||||
|
@ -44,6 +45,7 @@ type Parser struct {
|
||||||
timeFormat string
|
timeFormat string
|
||||||
timezone string
|
timezone string
|
||||||
defaultTags map[string]string
|
defaultTags map[string]string
|
||||||
|
strict bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(config *Config) (*Parser, error) {
|
func New(config *Config) (*Parser, error) {
|
||||||
|
@ -62,6 +64,7 @@ func New(config *Config) (*Parser, error) {
|
||||||
timeFormat: config.TimeFormat,
|
timeFormat: config.TimeFormat,
|
||||||
timezone: config.Timezone,
|
timezone: config.Timezone,
|
||||||
defaultTags: config.DefaultTags,
|
defaultTags: config.DefaultTags,
|
||||||
|
strict: config.Strict,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,7 +76,10 @@ func (p *Parser) parseArray(data []interface{}) ([]telegraf.Metric, error) {
|
||||||
case map[string]interface{}:
|
case map[string]interface{}:
|
||||||
metrics, err := p.parseObject(v)
|
metrics, err := p.parseObject(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
if p.strict {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
results = append(results, metrics...)
|
results = append(results, metrics...)
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -17,6 +17,7 @@ const (
|
||||||
validJSONArrayMultiple = "[{\"a\": 5, \"b\": {\"c\": 6}}, {\"a\": 7, \"b\": {\"c\": 8}}]"
|
validJSONArrayMultiple = "[{\"a\": 5, \"b\": {\"c\": 6}}, {\"a\": 7, \"b\": {\"c\": 8}}]"
|
||||||
invalidJSON = "I don't think this is JSON"
|
invalidJSON = "I don't think this is JSON"
|
||||||
invalidJSON2 = "{\"a\": 5, \"b\": \"c\": 6}}"
|
invalidJSON2 = "{\"a\": 5, \"b\": \"c\": 6}}"
|
||||||
|
mixedValidityJSON = "[{\"a\": 5, \"time\": \"2006-01-02T15:04:05\"}, {\"a\": 2}]"
|
||||||
)
|
)
|
||||||
|
|
||||||
const validJSONTags = `
|
const validJSONTags = `
|
||||||
|
@ -152,6 +153,41 @@ func TestParseInvalidJSON(t *testing.T) {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParseJSONImplicitStrictness(t *testing.T) {
|
||||||
|
parserImplicitNoStrict, err := New(&Config{
|
||||||
|
MetricName: "json_test",
|
||||||
|
TimeKey: "time",
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
_, err = parserImplicitNoStrict.Parse([]byte(mixedValidityJSON))
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseJSONExplicitStrictnessFalse(t *testing.T) {
|
||||||
|
parserNoStrict, err := New(&Config{
|
||||||
|
MetricName: "json_test",
|
||||||
|
TimeKey: "time",
|
||||||
|
Strict: false,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
_, err = parserNoStrict.Parse([]byte(mixedValidityJSON))
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseJSONExplicitStrictnessTrue(t *testing.T) {
|
||||||
|
parserStrict, err := New(&Config{
|
||||||
|
MetricName: "json_test",
|
||||||
|
TimeKey: "time",
|
||||||
|
Strict: true,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
_, err = parserStrict.Parse([]byte(mixedValidityJSON))
|
||||||
|
require.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
func TestParseWithTagKeys(t *testing.T) {
|
func TestParseWithTagKeys(t *testing.T) {
|
||||||
// Test that strings not matching tag keys are ignored
|
// Test that strings not matching tag keys are ignored
|
||||||
parser, err := New(&Config{
|
parser, err := New(&Config{
|
||||||
|
|
|
@ -89,6 +89,9 @@ type Config struct {
|
||||||
// default timezone
|
// default timezone
|
||||||
JSONTimezone string `toml:"json_timezone"`
|
JSONTimezone string `toml:"json_timezone"`
|
||||||
|
|
||||||
|
// Whether to continue if a JSON object can't be coerced
|
||||||
|
JSONStrict bool `toml:"json_strict"`
|
||||||
|
|
||||||
// Authentication file for collectd
|
// Authentication file for collectd
|
||||||
CollectdAuthFile string `toml:"collectd_auth_file"`
|
CollectdAuthFile string `toml:"collectd_auth_file"`
|
||||||
// One of none (default), sign, or encrypt
|
// One of none (default), sign, or encrypt
|
||||||
|
@ -164,6 +167,7 @@ func NewParser(config *Config) (Parser, error) {
|
||||||
TimeFormat: config.JSONTimeFormat,
|
TimeFormat: config.JSONTimeFormat,
|
||||||
Timezone: config.JSONTimezone,
|
Timezone: config.JSONTimezone,
|
||||||
DefaultTags: config.DefaultTags,
|
DefaultTags: config.DefaultTags,
|
||||||
|
Strict: config.JSONStrict,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
case "value":
|
case "value":
|
||||||
|
|
Loading…
Reference in New Issue