Skip to content

Feature/1363: Additional configuration for JSON parser#4351

Merged
danielnelson merged 21 commits intomasterfrom
feature/1363
Aug 23, 2018
Merged

Feature/1363: Additional configuration for JSON parser#4351
danielnelson merged 21 commits intomasterfrom
feature/1363

Conversation

@MrMaxBuilds
Copy link
Contributor

@MrMaxBuilds MrMaxBuilds commented Jun 27, 2018

closes: #2986

Required for all PRs:

  • Signed CLA.
  • Associated README.md updated.
  • Has appropriate unit tests.

func NewJSONParser(
metricName string,
tagKeys []string,
fieldKeys []string,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally we want to avoid changing exported function signatures, as it destabilizes packages. Perhaps what we could do is add a newJSONParser that takes different (or functional) parameters. We could then update those things that call NewJSONParser directly to call NewParser, which will in turn call newJSONParser.

@MrMaxBuilds MrMaxBuilds closed this Jul 2, 2018
@danielnelson
Copy link
Contributor

@maxunt As we discussed, let's have this be string_keys and list flattened paths to values that should be added as string fields.

@danielnelson danielnelson reopened this Jul 10, 2018
nTime := time.Now().UTC()
if p.JSONTimeKey != "" {
if p.JSONTimeFormat == "" {
err := fmt.Errorf("E! If 'json_time_key' is specified, there must be a 'json_time_format'")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is an error and not a log message, don't include the E!. As a nitpick, it would probably be good to try to come up with a shorter error string, perhaps: use of 'json_time_key' requires 'json_time_format'?

Remember to do this for the other errors, here is some more info on how "standard" Go error strings should look: https://github.com/golang/go/wiki/CodeReviewComments#error-strings


func (p *JSONParser) Parse(buf []byte) ([]telegraf.Metric, error) {

//if json_query is specified
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this comment, it doesn't provide any new information on why and the what can be determined by looking at the code.

## List of field names to extract from JSON and add as string fields
# string_fields = []

## gjson query path to specify a specific chunk of JSON to be parsed with the above configuration
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrap comments before 78 chars for readability.

return nil, err
}

nTime, err = time.Parse(p.JSONTimeFormat, f.Fields[p.JSONTimeKey].(string))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make sure to test the type assertion since it comes from input and could be any type.

}

_, err := parser.Parse([]byte(testString))
assert.Error(t, err)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use require instead of assert (almost) everywhere. Doesn't really matter here but this will prevent confusing chained errors.

metrics, err := parser.Parse([]byte(testString))
assert.NoError(t, err)
assert.Equal(t, metrics[0].Fields()["last"], "Murphy")
assert.Equal(t, 3, len(metrics))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check the length before indexing, this will make the test easier to understand if it fails.

}
metrics, err := parser.Parse([]byte(testString))
assert.NoError(t, err)
assert.Equal(t, false, metrics[0].Time() == metrics[1].Time())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check len(metrics) first, add some more tests to check error cases.


The JSON data format also supports extracting time values through the config "json_time_key" and "json_time_format".
If "json_time_key" is set, "json_time_format" must be specified. The "json_time_key" describes the name of the field containing time information. The "json_time_format" must be a recognized Go time format.
More info on time formats can be found here: https://golang.org/src/time/format.go
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use https://golang.org/pkg/time/#Parse as the link. This other link is still not great but the audience of this documentation is not Go developers.

Try to wrap around 78 chars

@danielnelson danielnelson added this to the 1.8.0 milestone Jul 16, 2018
@danielnelson danielnelson added the feat Improvement on an existing feature such as adding a new setting/mode to an existing plugin label Jul 16, 2018
@MrMaxBuilds MrMaxBuilds mentioned this pull request Jul 17, 2018

## gjson query path to specify a specific chunk of JSON to be parsed with
## the above configuration. If not specified, the whole file will be parsed.
## gjson query paths are described here:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add link to where gjson query paths are described

// MetricName applies to JSON & value. This will be the name of the measurement.
MetricName string

//holds a gjson path for json parser
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When adding comments, add a space before starting comment text (// This is my comment)

timeKey string,
timeFormat string,
defaultTags map[string]string,
) (Parser, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Returning an error isn't useful here. Where it's an unexported function not matching any interface, I'd remove it, or remove the function altogether and just instantiate a JSONParser on line 120

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No error is ever returned by the new_Parser functions, but i thought i'd include it here to match the format of the rest of the parsers


if len(metrics) < 1 {
return nil, fmt.Errorf("Can not parse the line: %s, for data format: influx ", line)
return nil, fmt.Errorf("can not parse the line: %s, for data format: influx ", line)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also the copypasta, s/influx/json

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you clarify what you mean here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace "influx" with "json", it appears to be copy paste code

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah i see. good catch thanks

json_time_key = "b_time"

## holds the format of timestamp to be parsed
json_time_format = "02 Jan 06 15:04 MST"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an interesting example time format because there it doesn't include a year. This results in a pared time that is in year 0. I think after parsing the time we should do something similar to what we did in logparser and use the current year. While this would be pretty annoying if you actually want to store in a metric from year 0, I think it is much more likely to be what is wanted:

if ts.Year() == 0 {
	ts = ts.AddDate(timestamp.Year(), 0, 0)
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat Improvement on an existing feature such as adding a new setting/mode to an existing plugin

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Make timestamp configurable for JSON parser

3 participants