Add strict mode to JSON parser (#6536)

This commit is contained in:
David McKay 2019-10-23 22:06:39 +01:00 committed by Daniel Nelson
parent 41d6a1a787
commit a9a0d4048a
4 changed files with 51 additions and 1 deletions

View File

@ -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.
## ##

View File

@ -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:

View File

@ -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{

View File

@ -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":