New JSON Parser (#9246)

This commit is contained in:
Sebastian Spaink 2021-06-10 14:22:18 -05:00 committed by GitHub
parent 62715d158b
commit 885252d388
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
44 changed files with 1981 additions and 43 deletions

View File

@ -24,6 +24,7 @@ import (
"github.com/influxdata/telegraf/plugins/inputs"
"github.com/influxdata/telegraf/plugins/outputs"
"github.com/influxdata/telegraf/plugins/parsers"
"github.com/influxdata/telegraf/plugins/parsers/json_v2"
"github.com/influxdata/telegraf/plugins/processors"
"github.com/influxdata/telegraf/plugins/serializers"
"github.com/influxdata/toml"
@ -1387,6 +1388,68 @@ func (c *Config) getParserConfig(name string, tbl *ast.Table) (*parsers.Config,
}
}
//for JSONPath parser
if node, ok := tbl.Fields["json_v2"]; ok {
if metricConfigs, ok := node.([]*ast.Table); ok {
pc.JSONV2Config = make([]parsers.JSONV2Config, len(metricConfigs))
for i, metricConfig := range metricConfigs {
mc := pc.JSONV2Config[i]
c.getFieldString(metricConfig, "measurement_name", &mc.MeasurementName)
if mc.MeasurementName == "" {
mc.MeasurementName = name
}
c.getFieldString(metricConfig, "measurement_name_path", &mc.MeasurementNamePath)
c.getFieldString(metricConfig, "timestamp_path", &mc.TimestampPath)
c.getFieldString(metricConfig, "timestamp_format", &mc.TimestampFormat)
c.getFieldString(metricConfig, "timestamp_timezone", &mc.TimestampTimezone)
if fieldConfigs, ok := metricConfig.Fields["field"]; ok {
if fieldConfigs, ok := fieldConfigs.([]*ast.Table); ok {
for _, fieldconfig := range fieldConfigs {
var f json_v2.DataSet
c.getFieldString(fieldconfig, "path", &f.Path)
c.getFieldString(fieldconfig, "rename", &f.Rename)
c.getFieldString(fieldconfig, "type", &f.Type)
mc.Fields = append(mc.Fields, f)
}
}
}
if fieldConfigs, ok := metricConfig.Fields["tag"]; ok {
if fieldConfigs, ok := fieldConfigs.([]*ast.Table); ok {
for _, fieldconfig := range fieldConfigs {
var t json_v2.DataSet
c.getFieldString(fieldconfig, "path", &t.Path)
c.getFieldString(fieldconfig, "rename", &t.Rename)
t.Type = "string"
mc.Tags = append(mc.Tags, t)
}
}
}
if objectconfigs, ok := metricConfig.Fields["object"]; ok {
if objectconfigs, ok := objectconfigs.([]*ast.Table); ok {
for _, objectConfig := range objectconfigs {
var o json_v2.JSONObject
c.getFieldString(objectConfig, "path", &o.Path)
c.getFieldString(objectConfig, "timestamp_key", &o.TimestampKey)
c.getFieldString(objectConfig, "timestamp_format", &o.TimestampFormat)
c.getFieldString(objectConfig, "timestamp_timezone", &o.TimestampTimezone)
c.getFieldBool(objectConfig, "disable_prepend_keys", &o.DisablePrependKeys)
c.getFieldStringSlice(objectConfig, "included_keys", &o.IncludedKeys)
c.getFieldStringSlice(objectConfig, "excluded_keys", &o.ExcludedKeys)
c.getFieldStringSlice(objectConfig, "tags", &o.Tags)
c.getFieldStringMap(objectConfig, "renames", &o.Renames)
c.getFieldStringMap(objectConfig, "fields", &o.Fields)
mc.JSONObjects = append(mc.JSONObjects, o)
}
}
}
pc.JSONV2Config[i] = mc
}
}
}
pc.MetricName = name
if c.hasErrs() {
@ -1494,7 +1557,7 @@ func (c *Config) missingTomlField(_ reflect.Type, key string) error {
"prefix", "prometheus_export_timestamp", "prometheus_sort_metrics", "prometheus_string_as_label",
"separator", "splunkmetric_hec_routing", "splunkmetric_multimetric", "tag_keys",
"tagdrop", "tagexclude", "taginclude", "tagpass", "tags", "template", "templates",
"value_field_name", "wavefront_source_override", "wavefront_use_strict", "xml":
"value_field_name", "wavefront_source_override", "wavefront_use_strict", "xml", "json_v2":
// ignore fields that are common to all plugins.
default:

12
go.mod
View File

@ -44,14 +44,14 @@ require (
github.com/denisenkom/go-mssqldb v0.9.0
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1
github.com/dimchansky/utfbom v1.1.1
github.com/docker/docker v20.10.5+incompatible
github.com/docker/docker v20.10.6+incompatible
github.com/dynatrace-oss/dynatrace-metric-utils-go v0.1.0
github.com/eclipse/paho.mqtt.golang v1.3.0
github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32
github.com/go-logfmt/logfmt v0.5.0
github.com/go-ping/ping v0.0.0-20210201095549-52eed920f98c
github.com/go-redis/redis v6.15.9+incompatible
github.com/go-sql-driver/mysql v1.5.0
github.com/go-sql-driver/mysql v1.6.0
github.com/goburrow/modbus v0.1.0 // indirect
github.com/goburrow/serial v0.1.0 // indirect
github.com/gobwas/glob v0.2.3
@ -103,7 +103,7 @@ require (
github.com/prometheus/client_golang v1.7.1
github.com/prometheus/client_model v0.2.0
github.com/prometheus/common v0.15.0
github.com/prometheus/procfs v0.2.0
github.com/prometheus/procfs v0.6.0
github.com/prometheus/prometheus v1.8.2-0.20200911110723-e83ef207b6c2
github.com/riemann/riemann-go-client v0.5.0
github.com/safchain/ethtool v0.0.0-20200218184317-f459e2d13664
@ -116,7 +116,7 @@ require (
github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271
github.com/stretchr/testify v1.7.0
github.com/tbrandon/mbserver v0.0.0-20170611213546-993e1772cc62
github.com/testcontainers/testcontainers-go v0.10.0
github.com/testcontainers/testcontainers-go v0.11.0
github.com/tidwall/gjson v1.6.0
github.com/tinylib/msgp v1.1.5
github.com/tklauser/go-sysconf v0.3.5 // indirect
@ -132,8 +132,8 @@ require (
go.uber.org/multierr v1.6.0 // indirect
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9
golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a
golang.org/x/sys v0.0.0-20210324051608-47abb6519492
golang.org/x/text v0.3.4
golang.org/x/tools v0.1.0
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200205215550-e35592f146e4

97
go.sum
View File

@ -121,9 +121,11 @@ github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3h
github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ=
github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8=
github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg=
github.com/Microsoft/hcsshim v0.8.15 h1:Aof83YILRs2Vx3GhHqlvvfyx1asRJKMFIMeVlHsZKtI=
github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00=
github.com/Microsoft/hcsshim v0.8.16 h1:8/auA4LFIZFTGrqfKhGBSXwM6/4X1fHa/xniyEHu8ac=
github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600=
github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU=
github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
@ -239,10 +241,16 @@ github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4Yn
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c=
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk=
github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk=
github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34=
github.com/caio/go-tdigest v3.1.0+incompatible h1:uoVMJ3Q5lXmVLCCqaMGHLBWnbGoN6Lpu7OAUPR60cds=
@ -276,13 +284,17 @@ github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMe
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE=
github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU=
github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU=
github.com/containerd/btrfs v0.0.0-20201111183144-404b9149801e/go.mod h1:jg2QkJcsabfHugurUvvPhS3E08Oxiuh5W/g1ybB4e0E=
github.com/containerd/btrfs v0.0.0-20210316141732-918d888fb676/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss=
github.com/containerd/cgroups v0.0.0-20190717030353-c4b9ac5c7601/go.mod h1:X9rLEHIqSf/wfK8NsPqxJmeZgW4pcfzdXITDrUSJ6uI=
github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko=
github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM=
github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo=
github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102 h1:Qf4HiqfvmB7zS6scsmNgTLmByHbq8n9RTF39v+TzP7A=
github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo=
github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68 h1:hkGVFjz+plgr5UfxZUTPFbUFIF/Km6/s+RVRIRHLrrY=
github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE=
github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE=
@ -294,24 +306,34 @@ github.com/containerd/containerd v1.3.1-0.20191213020239-082f7e3aed57/go.mod h1:
github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
github.com/containerd/containerd v1.3.4/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
github.com/containerd/containerd v1.4.0-beta.2.0.20200729163537-40b22ef07410/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
github.com/containerd/containerd v1.5.0-beta.1 h1:IK6yirB4X7wpKyFSikWiT++nZsyIxGAAgNEv3fEGuls=
github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
github.com/containerd/containerd v1.5.0-beta.1/go.mod h1:5HfvG1V2FsKesEGQ17k5/T7V960Tmcumvqn8Mc+pCYQ=
github.com/containerd/containerd v1.5.0-beta.3/go.mod h1:/wr9AVtEM7x9c+n0+stptlo/uBBoBORwEx6ardVcmKU=
github.com/containerd/containerd v1.5.0-beta.4 h1:zjz4MOAOFgdBlwid2nNUlJ3YLpVi/97L36lfMYJex60=
github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09ZvgqEq8EfBp/m3lcVZIvPHhI=
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo=
github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7 h1:6ejg6Lkk8dskcM7wQ28gONkukbQkM4qpj4RnYbpFzrI=
github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR3BEg7bDFaEddKm54WSmrol1fKWDU1nKYkgrcgZT7Y=
github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e h1:6JKvHHt396/qabvMhnhUZvWaHZzfVfldxE60TK8YLhg=
github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ=
github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0=
github.com/containerd/fifo v0.0.0-20201026212402-0724c46b320c/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0=
github.com/containerd/fifo v0.0.0-20210316144830-115abcc95a1d/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4=
github.com/containerd/go-cni v1.0.1/go.mod h1:+vUpYxKvAF72G9i1WoDOiPGRtQpqsNW/ZHtSlv++smU=
github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
github.com/containerd/go-runc v0.0.0-20190911050354-e029b79d8cda/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g=
github.com/containerd/go-runc v0.0.0-20201020171139-16b287bc67d0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok=
github.com/containerd/imgcrypt v1.0.1/go.mod h1:mdd8cEPW7TPgNG4FpuP3sGBiQ7Yi/zak9TYCG3juvb0=
github.com/containerd/imgcrypt v1.0.4-0.20210301171431-0ae5c75f59ba/go.mod h1:6TNsg0ctmizkrOgXRNQjAPFWpMYRWuiB6dSF4Pfa5SA=
github.com/containerd/imgcrypt v1.1.1-0.20210312161619-7ed62a527887/go.mod h1:5AZJNI6sLHJljKuI9IHnw1pWqo/F0nGDOuR9zgTs7ow=
github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFYfE5+So4M5syatU0N0f0LbWpuqyMi4/BE8c=
github.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY=
github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
github.com/containerd/ttrpc v0.0.0-20191028202541-4f1b8fe65a5c/go.mod h1:LPm1u0xBw8r8NOKoOdNMeVHSawSsltak+Ihv+etqsE8=
@ -321,10 +343,13 @@ github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kw
github.com/containerd/typeurl v0.0.0-20190911142611-5eb25027c9fd/go.mod h1:GeKYzf2pQcqv7tJ0AoCuuhtnqhva5LNU3U+OyKxxJpk=
github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg=
github.com/containerd/zfs v0.0.0-20200918131355-0a33824f23a2/go.mod h1:8IgZOBdv8fAgXddBT4dBXJPtxyRsejFIpXoklgxgEjw=
github.com/containerd/zfs v0.0.0-20210301145711-11e8f1707f62/go.mod h1:A9zfAbMlQwE+/is6hi0Xw8ktpL+6glmqZYtevJgaB8Y=
github.com/containerd/zfs v0.0.0-20210315114300-dde8f0fda960/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY=
github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
github.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHVlzhJpcY6TQxn/fUyDDM=
github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc=
github.com/containers/ocicrypt v1.1.0/go.mod h1:b8AOe0YR67uU8OqfVNcznfFpAzu3rdgUV4GP9qXPfu4=
github.com/coreos/etcd v3.3.22+incompatible h1:AnRMUyVdVvh1k7lHe61YEd227+CLoNogQuAypztGSK4=
github.com/coreos/etcd v3.3.22+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
@ -350,6 +375,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw=
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ=
github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s=
@ -381,8 +408,8 @@ github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v17.12.0-ce-rc1.0.20200706150819-a40b877fbb9e+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v20.10.5+incompatible h1:o5WL5onN4awYGwrW7+oTn5x9AF2prw7V0Ox8ZEkoCdg=
github.com/docker/docker v20.10.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v20.10.6+incompatible h1:oXI3Vas8TI8Eu/EjH4srKHJBVqraSzJybhxY7Om9faQ=
github.com/docker/docker v20.10.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
@ -539,8 +566,9 @@ github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGK
github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0=
@ -776,6 +804,7 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/influxdata/apcupsd v0.0.0-20210427145308-694d5caead0e h1:3J1OB4RDKwXs5l8uEV6BP/tucOJOPDQysiT7/9cuXzA=
github.com/influxdata/apcupsd v0.0.0-20210427145308-694d5caead0e/go.mod h1:WYK/Z/aXq9cbMFIL5ihcA4sX/r/3/WCas/Qvs/2fXcA=
@ -990,6 +1019,7 @@ github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3N
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
github.com/miekg/dns v1.1.31 h1:sJFOl9BgwbYAWOGEwr61FU28pqsBNdpRBnhGXtO06Oo=
github.com/miekg/dns v1.1.31/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721 h1:RlZweED6sbSArvlE924+mUcZuXKLBHA35U7LN621Bws=
github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721/go.mod h1:Ickgr2WtCLZ2MDGd4Gr0geeCH5HybhRJbonOgQpvSxc=
github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4=
@ -1009,14 +1039,20 @@ github.com/mitchellh/mapstructure v1.2.2 h1:dxe5oCinTXiTIcfgmZecdCzPmAJKd46KsCWc
github.com/mitchellh/mapstructure v1.2.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
github.com/moby/ipvs v1.0.1 h1:aoZ7fhLTXgDbzVrAnvV+XbKOU8kOET7B3+xULDF/1o0=
github.com/moby/ipvs v1.0.1 h1:aoZ7fhLTXgDbzVrAnvV+XbKOU8kOET7B3+xULDF/1o0=
github.com/moby/ipvs v1.0.1 h1:aoZ7fhLTXgDbzVrAnvV+XbKOU8kOET7B3+xULDF/1o0=
github.com/moby/ipvs v1.0.1/go.mod h1:2pngiyseZbIKXNv7hsKj3O9UEz30c53MT9005gt2hxQ=
github.com/moby/ipvs v1.0.1/go.mod h1:2pngiyseZbIKXNv7hsKj3O9UEz30c53MT9005gt2hxQ=
github.com/moby/ipvs v1.0.1/go.mod h1:2pngiyseZbIKXNv7hsKj3O9UEz30c53MT9005gt2hxQ=
github.com/moby/sys/mount v0.2.0 h1:WhCW5B355jtxndN5ovugJlMFJawbUODuW8fSnEH6SSM=
github.com/moby/sys/mount v0.2.0/go.mod h1:aAivFE2LB3W4bACsUXChRHQ0qKWsetY4Y9V7sxOougM=
github.com/moby/sys/mountinfo v0.4.0 h1:1KInV3Huv18akCu58V7lzNlt+jFmqlu1EaErnEHE/VM=
github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A=
github.com/moby/sys/mountinfo v0.4.1 h1:1O+1cHA1aujwEwwVMa2Xm2l+gIpUHyd3+D+d7LZh1kM=
github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A=
github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ=
github.com/moby/term v0.0.0-20200312100748-672ec06f55cd h1:aY7OQNf2XqY/JQ6qREWamhI/81os/agb2BAGpcx5yWI=
github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo=
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 h1:rzf0wL0CHVc8CEsgyygG0Mn9CNCCPZqOPaz8RiiHYQk=
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@ -1193,8 +1229,9 @@ github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDa
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/prometheus/procfs v0.0.11/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.2.0 h1:wH4vA7pcjKuZzjF7lM8awk4fnuJO6idemZXoKnULUx4=
github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/prometheus v1.8.2-0.20200911110723-e83ef207b6c2 h1:IB/5RJRcJiR/YzKs4Aou86s/RaMepZOZVCArYNHJHWc=
github.com/prometheus/prometheus v1.8.2-0.20200911110723-e83ef207b6c2/go.mod h1:Td6hjwdXDmVt5CI9T03Sw+yBNxLBq/Yx3ZtmtP8zlCA=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
@ -1262,6 +1299,13 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx
github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
@ -1281,11 +1325,14 @@ github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU
github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8=
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271 h1:WhxRHzgeVGETMlmVfqhRn8RIeeNoPr2Czh33I4Zdccw=
github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
@ -1312,8 +1359,8 @@ github.com/tbrandon/mbserver v0.0.0-20170611213546-993e1772cc62 h1:Oj2e7Sae4XrOs
github.com/tbrandon/mbserver v0.0.0-20170611213546-993e1772cc62/go.mod h1:qUzPVlSj2UgxJkVbH0ZwuuiR46U8RBMDT5KLY78Ifpw=
github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I=
github.com/tedsuo/ifrit v0.0.0-20180802180643-bea94bb476cc/go.mod h1:eyZnKCc955uh98WQvzOm0dgAeLnf2O0Rz0LPoC5ze+0=
github.com/testcontainers/testcontainers-go v0.10.0 h1:ASWe0nwTNg5z8K3WSQ8aBNB6j5vrNJocFPEZF4NS0qI=
github.com/testcontainers/testcontainers-go v0.10.0/go.mod h1:zFYk0JndthnMHEwtVRHCpLwIP/Ik1G7mvIAQ2MdZ+Ig=
github.com/testcontainers/testcontainers-go v0.11.0 h1:HO5YOx2DYBHqcg4MzVWPj3FuHAv7USWVu94vCSsgiaM=
github.com/testcontainers/testcontainers-go v0.11.0/go.mod h1:HztBCODzuA+YpMXGK8amjO8j50jz2gcT0BOzSKUiYIs=
github.com/tidwall/gjson v1.6.0 h1:9VEQWz6LLMUsUl6PueE49ir4Ka6CzLymOAZDxpFsTDc=
github.com/tidwall/gjson v1.6.0/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls=
github.com/tidwall/match v1.0.1 h1:PnKP62LPNxHKTwvHHZZzdOAOCtsJTjo6dZLCwpKm5xc=
@ -1343,6 +1390,10 @@ github.com/vaughan0/go-ini v0.0.0-20130923145212-a98ad7ee00ec/go.mod h1:owBmyHYM
github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0=
github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0=
github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0=
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI=
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7ZovXvuNyL3XQl8UFofeikI1NW1Gypu7k=
@ -1390,6 +1441,7 @@ go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qL
go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
go.mongodb.org/mongo-driver v1.3.0/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE=
go.mongodb.org/mongo-driver v1.3.2/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE=
go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk=
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
@ -1440,6 +1492,7 @@ golang.org/x/crypto v0.0.0-20200204104054-c9f3fb736b72/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
@ -1532,6 +1585,7 @@ golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81R
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@ -1549,8 +1603,9 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a h1:DcqTD9SDLc+1P/r1EmRBwnVsrOwW+kk2vWf9n+1sGhs=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -1618,10 +1673,13 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200821140526-fda516888d29/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200828194041-157a740278f4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -1630,12 +1688,18 @@ golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa h1:ZYxPR6aca/uhfRJyaOAtflSHjJYiktO7QnJC5ut7iY4=
golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210324051608-47abb6519492 h1:Paq34FxTluEPvVyayQqMPgHm+vTOrIifmcYxFBx9TLg=
golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@ -1808,6 +1872,7 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.37.0 h1:uSZWeQJX5j11bIQ4AJoj+McDBo29cY1MCoC1wO3ts+c=
google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
@ -1877,6 +1942,7 @@ gopkg.in/sourcemap.v1 v1.0.5 h1:inv58fC9f9J3TK2Y2R1NPntXEn3/wjWHkonhIUODNTI=
gopkg.in/sourcemap.v1 v1.0.5/go.mod h1:2RlvNNSMglmRrcvhfuzp4hQHwOtjxlbjX7UPY/GXb78=
gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/tomb.v1 v1.0.0-20140529071818-c131134a1947/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
@ -1899,8 +1965,9 @@ gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclp
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
gotest.tools/v3 v3.0.2 h1:kG1BFyqVHuQoVQiR1bWGnfz/fmHvvuiSPIV7rvl360E=
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0=
gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@ -0,0 +1,187 @@
# JSON Parser - Version 2
This parser takes valid JSON input and turns it into metrics. The query syntax supported is [GJSON Path Syntax](https://github.com/tidwall/gjson/blob/v1.7.5/SYNTAX.md), you can go to this playground to test out your GJSON path here: https://gjson.dev/. You can find multiple examples under the `testdata` folder.
## Configuration
You configure this parser by describing the metric you want by defining the fields and tags from the input. The configuration is divided into config sub-tables called `field`, `tag`, and `object`. In the example below you can see all the possible configuration keys you can define for each config table. In the sections that follow these configuration keys are defined in more detail.
**Example configuration:**
```toml
[[inputs.file]]
urls = []
data_format = "json_v2"
[[inputs.file.json_v2]]
measurement_name = "" # A string that will become the new measurement name
measurement_name_path = "" # A string with valid GJSON path syntax, will override measurement_name
timestamp_path = "" # A string with valid GJSON path syntax to a valid timestamp (single value)
timestamp_format = "" # A string with a valid timestamp format (see below for possible values)
timestamp_timezone = "" # A string with with a valid timezone (see below for possible values)
[[inputs.file.json_v2.tag]]
path = "" # A string with valid GJSON path syntax
rename = "new name" # A string with a new name for the tag key
[[inputs.file.json_v2.field]]
path = "" # A string with valid GJSON path syntax
rename = "new name" # A string with a new name for the tag key
type = "int" # A string specifying the type (int,uint,float,string,bool)
[[inputs.file.json_v2.object]]
path = "" # A string with valid GJSON path syntax
timestamp_key = "" # A JSON key (for a nested key, prepend the parent keys with underscores) to a valid timestamp
timestamp_format = "" # A string with a valid timestamp format (see below for possible values)
timestamp_timezone = "" # A string with with a valid timezone (see below for possible values)
disable_prepend_keys = false (or true, just not both)
included_keys = [] # List of JSON keys (for a nested key, prepend the parent keys with underscores) that should be only included in result
excluded_keys = [] # List of JSON keys (for a nested key, prepend the parent keys with underscores) that shouldn't be included in result
tags = [] # List of JSON keys (for a nested key, prepend the parent keys with underscores) to be a tag instead of a field
[inputs.file.json_v2.object.renames] # A map of JSON keys (for a nested key, prepend the parent keys with underscores) with a new name for the tag key
key = "new name"
[inputs.file.json_v2.object.fields] # A map of JSON keys (for a nested key, prepend the parent keys with underscores) with a type (int,uint,float,string,bool)
key = "int"
```
---
### root config options
* **measurement_name (OPTIONAL)**: Will set the measurement name to the provided string.
* **measurement_name_path (OPTIONAL)**: You can define a query with [GJSON Path Syntax](https://github.com/tidwall/gjson/blob/v1.7.5/SYNTAX.md) to set a measurement name from the JSON input. The query must return a single data value or it will use the default measurement name. This takes precedence over `measurement_name`.
* **timestamp_path (OPTIONAL)**: You can define a query with [GJSON Path Syntax](https://github.com/tidwall/gjson/blob/v1.7.5/SYNTAX.md) to set a timestamp from the JSON input. The query must return a single data value or it will default to the current time.
* **timestamp_format (OPTIONAL, but REQUIRED when timestamp_query is defined**: Must be set to `unix`, `unix_ms`, `unix_us`, `unix_ns`, or
the Go "reference time" which is defined to be the specific time:
`Mon Jan 2 15:04:05 MST 2006`
* **timestamp_timezone (OPTIONAL, but REQUIRES timestamp_query**: This option should be set to a
[Unix TZ value](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones),
such as `America/New_York`, to `Local` to utilize the system timezone, or to `UTC`. Defaults to `UTC`
---
### `field` and `tag` config options
`field` and `tag` represent the elements of [line protocol](https://docs.influxdata.com/influxdb/v2.0/reference/syntax/line-protocol/), which is used to define a `metric`. You can use the `field` and `tag` config tables to gather a single value or an array of values that all share the same type and name. With this you can add a field or tag to a metric from data stored anywhere in your JSON. If you define the GJSON path to return a single value then you will get a single resutling metric that contains the field/tag. If you define the GJSON path to return an array of values, then each field/tag will be put into a separate metric (you use the # character to retrieve JSON arrays, find examples [here](https://github.com/tidwall/gjson/blob/v1.7.5/SYNTAX.md#arrays)).
Note that objects are handled separately, therefore if you provide a path that returns a object it will be ignored. You will need use the `object` config table to parse objects, because `field` and `tag` doesn't handle relationships between data. Each `field` and `tag` you define is handled as a separate data point.
The notable difference between `field` and `tag`, is that `tag` values will always be type string while `field` can be multiple types. You can define the type of `field` to be any [type that line protocol supports](https://docs.influxdata.com/influxdb/v2.0/reference/syntax/line-protocol/#data-types-and-format), which are:
* float
* int
* uint
* string
* bool
#### **field**
* **path (REQUIRED)**: You must define the path query that gathers the object with [GJSON Path Syntax](https://github.com/tidwall/gjson/blob/v1.7.5/SYNTAX.md).
* **name (OPTIONAL)**: You can define a string value to set the field name. If not defined it will use the trailing word from the provided query.
* **type (OPTIONAL)**: You can define a string value to set the desired type (float, int, uint, string, bool). If not defined it won't enforce a type and default to using the original type defined in the JSON (bool, float, or string).
#### **tag**
* **path (REQUIRED)**: You must define the path query that gathers the object with [GJSON Path Syntax](https://github.com/tidwall/gjson/blob/v1.7.5/SYNTAX.md).
* **name (OPTIONAL)**: You can define a string value to set the field name. If not defined it will use the trailing word from the provided query.
For good examples in using `field` and `tag` you can reference the following example configs:
* [fields_and_tags](testdata/fields_and_tags/telegraf.conf)
---
### object
With the configuration section `object`, you can gather metrics from [JSON objects](https://www.w3schools.com/js/js_json_objects.asp).
The following keys can be set for `object`:
* **path (REQUIRED)**: You must define the path query that gathers the object with [GJSON Path Syntax](https://github.com/tidwall/gjson/blob/v1.7.5/SYNTAX.md)
* **timestamp_key(OPTIONAL)**: You can define a json key (for a nested key, prepend the parent keys with underscores) for the value to be set as the timestamp from the JSON input.
* **timestamp_format (OPTIONAL, but REQUIRED when timestamp_query is defined**: Must be set to `unix`, `unix_ms`, `unix_us`, `unix_ns`, or
the Go "reference time" which is defined to be the specific time:
`Mon Jan 2 15:04:05 MST 2006`
* **timestamp_timezone (OPTIONAL, but REQUIRES timestamp_query**: This option should be set to a
[Unix TZ value](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones),
such as `America/New_York`, to `Local` to utilize the system timezone, or to `UTC`. Defaults to `UTC`
* **disable_prepend_keys (OPTIONAL)**: Set to true to prevent resulting nested data to contain the parent key prepended to its key **NOTE**: duplicate names can overwrite each other when this is enabled
* **included_keys (OPTIONAL)**: You can define a list of key's that should be the only data included in the metric, by default it will include everything.
* **excluded_keys (OPTIONAL)**: You can define json keys to be excluded in the metric, for a nested key, prepend the parent keys with underscores
* **tags (OPTIONAL)**: You can define json keys to be set as tags instead of fields, if you define a key that is an array or object then all nested values will become a tag
* **renames (OPTIONAL)**: A table matching the json key with the desired name (oppossed to defaulting to using the key), use names that include the prepended keys of its parent keys for nested results
* **fields (OPTIONAL)**: A table matching the json key with the desired type (int,string,bool,float), if you define a key that is an array or object then all nested values will become that type
## Arrays and Objects
The following describes the high-level approach when parsing arrays and objects:
**Array**: Every element in an array is treated as a *separate* metric
**Object**: Every key/value in a object is treated as a *single* metric
When handling nested arrays and objects, these above rules continue to apply as the parser creates metrics. When an object has multiple array's as values, the array's will become separate metrics containing only non-array values from the obejct. Below you can see an example of this behavior, with an input json containing an array of book objects that has a nested array of characters.
Example JSON:
```json
{
"book": {
"title": "The Lord Of The Rings",
"chapters": [
"A Long-expected Party",
"The Shadow of the Past"
],
"author": "Tolkien",
"characters": [
{
"name": "Bilbo",
"species": "hobbit"
},
{
"name": "Frodo",
"species": "hobbit"
}
],
"random": [
1,
2
]
}
}
```
Example configuration:
```toml
[[inputs.file]]
files = ["./testdata/multiple_arrays_in_object/input.json"]
data_format = "json_v2"
[[inputs.file.json_v2]]
[[inputs.file.json_v2.object]]
path = "book"
tags = ["title"]
disable_prepend_keys = true
```
Expected metrics:
```
file,title=The\ Lord\ Of\ The\ Rings author="Tolkien",chapters="A Long-expected Party"
file,title=The\ Lord\ Of\ The\ Rings author="Tolkien",chapters="The Shadow of the Past"
file,title=The\ Lord\ Of\ The\ Rings author="Tolkien",name="Bilbo",species="hobbit"
file,title=The\ Lord\ Of\ The\ Rings author="Tolkien",name="Frodo",species="hobbit"
file,title=The\ Lord\ Of\ The\ Rings author="Tolkien",random=1
file,title=The\ Lord\ Of\ The\ Rings author="Tolkien",random=2
```
You can find more complicated examples under the folder `testdata`.
## Types
For each field you have the option to define the types for each metric. The following rules are in place for this configuration:
* If a type is explicitly defined, the parser will enforce this type and convert the data to the defined type if possible. If the type can't be converted then the parser will fail.
* If a type isn't defined, the parser will use the default type defined in the JSON (int, float, string)
The type values you can set:
* `int`, bool, floats or strings (with valid numbers) can be converted to a int.
* `uint`, bool, floats or strings (with valid numbers) can be converted to a uint.
* `string`, any data can be formatted as a string.
* `float`, string values (with valid numbers) or integers can be converted to a float.
* `bool`, the string values "true" or "false" (regardless of capitalization) or the integer values `0` or `1` can be turned to a bool.

View File

@ -0,0 +1,587 @@
package json_v2
import (
"fmt"
"strconv"
"strings"
"time"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/internal"
"github.com/influxdata/telegraf/metric"
"github.com/tidwall/gjson"
)
type Parser struct {
Configs []Config
DefaultTags map[string]string
Log telegraf.Logger
Timestamp time.Time
measurementName string
iterateObjects bool
currentSettings JSONObject
}
type Config struct {
MeasurementName string `toml:"measurement_name"` // OPTIONAL
MeasurementNamePath string `toml:"measurement_name_path"` // OPTIONAL
TimestampPath string `toml:"timestamp_path"` // OPTIONAL
TimestampFormat string `toml:"timestamp_format"` // OPTIONAL, but REQUIRED when timestamp_path is defined
TimestampTimezone string `toml:"timestamp_timezone"` // OPTIONAL, but REQUIRES timestamp_path
Fields []DataSet
Tags []DataSet
JSONObjects []JSONObject
}
type DataSet struct {
Path string `toml:"path"` // REQUIRED
Type string `toml:"type"` // OPTIONAL, can't be set for tags they will always be a string
Rename string `toml:"rename"` // OPTIONAL
}
type JSONObject struct {
Path string `toml:"path"` // REQUIRED
TimestampKey string `toml:"timestamp_key"` // OPTIONAL
TimestampFormat string `toml:"timestamp_format"` // OPTIONAL, but REQUIRED when timestamp_path is defined
TimestampTimezone string `toml:"timestamp_timezone"` // OPTIONAL, but REQUIRES timestamp_path
Renames map[string]string `toml:"renames"` // OPTIONAL
Fields map[string]string `toml:"fields"` // OPTIONAL
Tags []string `toml:"tags"` // OPTIONAL
IncludedKeys []string `toml:"included_keys"` // OPTIONAL
ExcludedKeys []string `toml:"excluded_keys"` // OPTIONAL
DisablePrependKeys bool `toml:"disable_prepend_keys"` // OPTIONAL
}
type MetricNode struct {
OutputName string
SetName string
Tag bool
DesiredType string // Can be "int", "uint", "float", "bool", "string"
Metric telegraf.Metric
gjson.Result
}
func (p *Parser) Parse(input []byte) ([]telegraf.Metric, error) {
// Only valid JSON is supported
if !gjson.Valid(string(input)) {
return nil, fmt.Errorf("Invalid JSON provided, unable to parse")
}
var metrics []telegraf.Metric
for _, c := range p.Configs {
// Measurement name configuration
p.measurementName = c.MeasurementName
if c.MeasurementNamePath != "" {
result := gjson.GetBytes(input, c.MeasurementNamePath)
if !result.IsArray() && !result.IsObject() {
p.measurementName = result.String()
}
}
// Timestamp configuration
p.Timestamp = time.Now()
if c.TimestampPath != "" {
result := gjson.GetBytes(input, c.TimestampPath)
if !result.IsArray() && !result.IsObject() {
if c.TimestampFormat == "" {
err := fmt.Errorf("use of 'timestamp_query' requires 'timestamp_format'")
return nil, err
}
var err error
p.Timestamp, err = internal.ParseTimestamp(c.TimestampFormat, result.Value(), c.TimestampTimezone)
if err != nil {
return nil, err
}
}
}
fields, err := p.processMetric(c.Fields, input, false)
if err != nil {
return nil, err
}
tags, err := p.processMetric(c.Tags, input, true)
if err != nil {
return nil, err
}
objects, err := p.processObjects(c.JSONObjects, input)
if err != nil {
return nil, err
}
metrics = append(metrics, cartesianProduct(tags, fields)...)
if len(objects) != 0 && len(metrics) != 0 {
metrics = append(metrics, cartesianProduct(objects, metrics)...)
} else {
metrics = append(metrics, objects...)
}
}
for k, v := range p.DefaultTags {
for _, t := range metrics {
t.AddTag(k, v)
}
}
return metrics, nil
}
// processMetric will iterate over all 'field' or 'tag' configs and create metrics for each
// A field/tag can either be a single value or an array of values, each resulting in its own metric
// For multiple configs, a set of metrics is created from the cartesian product of each separate config
func (p *Parser) processMetric(data []DataSet, input []byte, tag bool) ([]telegraf.Metric, error) {
if len(data) == 0 {
return nil, nil
}
p.iterateObjects = false
var metrics [][]telegraf.Metric
for _, c := range data {
if c.Path == "" {
return nil, fmt.Errorf("GJSON path is required")
}
result := gjson.GetBytes(input, c.Path)
if result.IsObject() {
p.Log.Debugf("Found object in the path: %s, ignoring it please use 'object' to gather metrics from objects", c.Path)
continue
}
setName := c.Rename
// Default to the last path word, should be the upper key name
if setName == "" {
s := strings.Split(c.Path, ".")
setName = s[len(s)-1]
}
setName = strings.ReplaceAll(setName, " ", "_")
mNode := MetricNode{
OutputName: setName,
SetName: setName,
DesiredType: c.Type,
Tag: tag,
Metric: metric.New(
p.measurementName,
map[string]string{},
map[string]interface{}{},
p.Timestamp,
),
Result: result,
}
// Expand all array's and nested arrays into separate metrics
nodes, err := p.expandArray(mNode)
if err != nil {
return nil, err
}
var m []telegraf.Metric
for _, n := range nodes {
m = append(m, n.Metric)
}
metrics = append(metrics, m)
}
for i := 1; i < len(metrics); i++ {
metrics[i] = cartesianProduct(metrics[i-1], metrics[i])
}
return metrics[len(metrics)-1], nil
}
func cartesianProduct(a, b []telegraf.Metric) []telegraf.Metric {
if len(a) == 0 {
return b
}
if len(b) == 0 {
return a
}
p := make([]telegraf.Metric, len(a)*len(b))
i := 0
for _, a := range a {
for _, b := range b {
m := a.Copy()
mergeMetric(b, m)
p[i] = m
i++
}
}
return p
}
func mergeMetric(a telegraf.Metric, m telegraf.Metric) {
for _, f := range a.FieldList() {
m.AddField(f.Key, f.Value)
}
for _, t := range a.TagList() {
m.AddTag(t.Key, t.Value)
}
}
// expandArray will recursively create a new MetricNode for each element in a JSON array or single value
func (p *Parser) expandArray(result MetricNode) ([]MetricNode, error) {
var results []MetricNode
if result.IsObject() {
if !p.iterateObjects {
p.Log.Debugf("Found object in query ignoring it please use 'object' to gather metrics from objects")
return results, nil
}
r, err := p.combineObject(result)
if err != nil {
return nil, err
}
results = append(results, r...)
return results, nil
}
if result.IsArray() {
var err error
result.ForEach(func(_, val gjson.Result) bool {
m := metric.New(
p.measurementName,
map[string]string{},
map[string]interface{}{},
p.Timestamp,
)
if val.IsObject() {
if p.iterateObjects {
n := MetricNode{
SetName: result.SetName,
Metric: m,
Result: val,
}
var r []MetricNode
r, err = p.combineObject(n)
if err != nil {
return false
}
results = append(results, r...)
} else {
p.Log.Debugf("Found object in query ignoring it please use 'object' to gather metrics from objects")
}
if len(results) != 0 {
for _, newResult := range results {
mergeMetric(result.Metric, newResult.Metric)
}
}
return true
}
for _, f := range result.Metric.FieldList() {
m.AddField(f.Key, f.Value)
}
for _, f := range result.Metric.TagList() {
m.AddTag(f.Key, f.Value)
}
n := MetricNode{
Tag: result.Tag,
DesiredType: result.DesiredType,
OutputName: result.OutputName,
SetName: result.SetName,
Metric: m,
Result: val,
}
var r []MetricNode
r, err = p.expandArray(n)
if err != nil {
return false
}
results = append(results, r...)
return true
})
if err != nil {
return nil, err
}
} else {
if !result.Tag && !result.IsObject() {
if result.SetName == p.currentSettings.TimestampKey {
if p.currentSettings.TimestampFormat == "" {
err := fmt.Errorf("use of 'timestamp_query' requires 'timestamp_format'")
return nil, err
}
timestamp, err := internal.ParseTimestamp(p.currentSettings.TimestampFormat, result.Value(), p.currentSettings.TimestampTimezone)
if err != nil {
return nil, err
}
result.Metric.SetTime(timestamp)
} else {
v, err := p.convertType(result.Value(), result.DesiredType, result.SetName)
if err != nil {
return nil, err
}
result.Metric.AddField(result.OutputName, v)
}
} else if !result.IsObject() {
v, err := p.convertType(result.Value(), "string", result.SetName)
if err != nil {
return nil, err
}
result.Metric.AddTag(result.OutputName, v.(string))
}
results = append(results, result)
}
return results, nil
}
// processObjects will iterate over all 'object' configs and create metrics for each
func (p *Parser) processObjects(objects []JSONObject, input []byte) ([]telegraf.Metric, error) {
p.iterateObjects = true
var t []telegraf.Metric
for _, c := range objects {
p.currentSettings = c
if c.Path == "" {
return nil, fmt.Errorf("GJSON path is required")
}
result := gjson.GetBytes(input, c.Path)
if result.Type == gjson.Null {
return nil, fmt.Errorf("GJSON Path returned null")
}
rootObject := MetricNode{
Metric: metric.New(
p.measurementName,
map[string]string{},
map[string]interface{}{},
p.Timestamp,
),
Result: result,
}
metrics, err := p.expandArray(rootObject)
if err != nil {
return nil, err
}
for _, m := range metrics {
t = append(t, m.Metric)
}
}
return t, nil
}
// combineObject will add all fields/tags to a single metric
// If the object has multiple array's as elements it won't comine those, they will remain separate metrics
func (p *Parser) combineObject(result MetricNode) ([]MetricNode, error) {
var results []MetricNode
if result.IsArray() || result.IsObject() {
var err error
var prevArray bool
result.ForEach(func(key, val gjson.Result) bool {
// Determine if field/tag set name is configured
var setName string
if result.SetName != "" {
setName = result.SetName + "_" + strings.ReplaceAll(key.String(), " ", "_")
} else {
setName = strings.ReplaceAll(key.String(), " ", "_")
}
if p.isExcluded(setName) || !p.isIncluded(setName, val) {
return true
}
var outputName string
if p.currentSettings.DisablePrependKeys {
outputName = strings.ReplaceAll(key.String(), " ", "_")
} else {
outputName = setName
}
for k, n := range p.currentSettings.Renames {
if k == setName {
outputName = n
break
}
}
arrayNode := MetricNode{
DesiredType: result.DesiredType,
Tag: result.Tag,
OutputName: outputName,
SetName: setName,
Metric: result.Metric,
Result: val,
}
for k, t := range p.currentSettings.Fields {
if setName == k {
arrayNode.DesiredType = t
break
}
}
tag := false
for _, t := range p.currentSettings.Tags {
if setName == t {
tag = true
break
}
}
arrayNode.Tag = tag
if val.IsObject() {
prevArray = false
_, err = p.combineObject(arrayNode)
if err != nil {
return false
}
} else {
var r []MetricNode
r, err = p.expandArray(arrayNode)
if err != nil {
return false
}
if prevArray {
if !arrayNode.IsArray() {
// If another non-array element was found, merge it into all previous gathered metrics
if len(results) != 0 {
for _, newResult := range results {
mergeMetric(result.Metric, newResult.Metric)
}
}
} else {
// Multiple array's won't be merged but kept separate, add additional metrics gathered from an array
results = append(results, r...)
}
} else {
// Continue using the same metric if its an object
results = r
}
if val.IsArray() {
prevArray = true
}
}
return true
})
if err != nil {
return nil, err
}
}
return results, nil
}
func (p *Parser) isIncluded(key string, val gjson.Result) bool {
if len(p.currentSettings.IncludedKeys) == 0 {
return true
}
for _, i := range p.currentSettings.IncludedKeys {
if i == key {
return true
}
if val.IsArray() || val.IsObject() {
// Check if the included key is a sub element
if strings.HasPrefix(i, key) {
return true
}
}
}
return false
}
func (p *Parser) isExcluded(key string) bool {
for _, i := range p.currentSettings.ExcludedKeys {
if i == key {
return true
}
}
return false
}
func (p *Parser) ParseLine(line string) (telegraf.Metric, error) {
return nil, fmt.Errorf("ParseLine is designed for parsing influx line protocol, therefore not implemented for parsing JSON")
}
func (p *Parser) SetDefaultTags(tags map[string]string) {
p.DefaultTags = tags
}
// convertType will convert the value parsed from the input JSON to the specified type in the config
func (p *Parser) convertType(input interface{}, desiredType string, name string) (interface{}, error) {
switch inputType := input.(type) {
case string:
if desiredType != "string" {
switch desiredType {
case "uint":
r, err := strconv.ParseUint(inputType, 10, 64)
if err != nil {
return nil, fmt.Errorf("Unable to convert field '%s' to type uint: %v", name, err)
}
return r, nil
case "int":
r, err := strconv.Atoi(inputType)
if err != nil {
return nil, fmt.Errorf("Unable to convert field '%s' to type int: %v", name, err)
}
return r, nil
case "float":
r, err := strconv.ParseFloat(inputType, 64)
if err != nil {
return nil, fmt.Errorf("Unable to convert field '%s' to type float: %v", name, err)
}
return r, nil
case "bool":
r, err := strconv.ParseBool(inputType)
if err != nil {
return nil, fmt.Errorf("Unable to convert field '%s' to type bool: %v", name, err)
}
return r, nil
}
}
case bool:
switch desiredType {
case "string":
return strconv.FormatBool(inputType), nil
case "int":
if inputType {
return int64(1), nil
}
return int64(0), nil
case "uint":
if inputType {
return uint64(1), nil
}
return uint64(0), nil
}
case float64:
if desiredType != "float" {
switch desiredType {
case "string":
return fmt.Sprint(inputType), nil
case "int":
return int64(inputType), nil
case "uint":
return uint64(inputType), nil
case "bool":
if inputType == 0 {
return false, nil
} else if inputType == 1 {
return true, nil
} else {
return nil, fmt.Errorf("Unable to convert field '%s' to type bool", name)
}
}
}
default:
return nil, fmt.Errorf("unknown format '%T' for field '%s'", inputType, name)
}
return input, nil
}

View File

@ -0,0 +1,130 @@
package json_v2_test
import (
"bufio"
"fmt"
"io/ioutil"
"os"
"testing"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/config"
"github.com/influxdata/telegraf/plugins/inputs"
"github.com/influxdata/telegraf/plugins/inputs/file"
"github.com/influxdata/telegraf/plugins/parsers/influx"
"github.com/influxdata/telegraf/testutil"
"github.com/stretchr/testify/require"
)
func TestData(t *testing.T) {
var tests = []struct {
name string
test string
}{
{
name: "Test using just fields and tags",
test: "fields_and_tags",
},
{
name: "Test gathering from array of nested objects",
test: "nested_array_of_objects",
},
{
name: "Test setting timestamp",
test: "timestamp",
},
{
name: "Test setting measurement name from int",
test: "measurement_name_int",
},
{
name: "Test multiple types",
test: "types",
},
{
name: "Test settings tags in nested object",
test: "nested_tags",
},
{
name: "Test settings tags in nested and non-nested objects",
test: "nested_and_nonnested_tags",
},
{
name: "Test a more complex nested tag retrieval",
test: "nested_tags_complex",
},
{
name: "Test multiple arrays in object",
test: "multiple_arrays_in_object",
},
{
name: "Test fields and tags complex",
test: "fields_and_tags_complex",
},
{
name: "Test object",
test: "object",
},
{
name: "Test multiple timestamps",
test: "multiple_timestamps",
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
// Process the telegraf config file for the test
buf, err := ioutil.ReadFile(fmt.Sprintf("testdata/%s/telegraf.conf", tc.test))
require.NoError(t, err)
inputs.Add("file", func() telegraf.Input {
return &file.File{}
})
cfg := config.NewConfig()
err = cfg.LoadConfigData(buf)
require.NoError(t, err)
// Gather the metrics from the input file configure
acc := testutil.Accumulator{}
for _, i := range cfg.Inputs {
err = i.Init()
require.NoError(t, err)
err = i.Gather(&acc)
require.NoError(t, err)
}
require.NoError(t, err)
// Process expected metrics and compare with resulting metrics
expectedOutputs, err := readMetricFile(fmt.Sprintf("testdata/%s/expected.out", tc.test))
require.NoError(t, err)
testutil.RequireMetricsEqual(t, expectedOutputs, acc.GetTelegrafMetrics(), testutil.IgnoreTime())
})
}
}
func readMetricFile(path string) ([]telegraf.Metric, error) {
var metrics []telegraf.Metric
expectedFile, err := os.Open(path)
if err != nil {
return metrics, err
}
defer expectedFile.Close()
parser := influx.NewParser(influx.NewMetricHandler())
scanner := bufio.NewScanner(expectedFile)
for scanner.Scan() {
line := scanner.Text()
if line != "" {
m, err := parser.ParseLine(line)
if err != nil {
return nil, fmt.Errorf("unable to parse metric in %q failed: %v", line, err)
}
metrics = append(metrics, m)
}
}
err = expectedFile.Close()
if err != nil {
return metrics, err
}
return metrics, nil
}

View File

@ -0,0 +1,2 @@
file,status=200 duration=2i,json_duration=100
file,status=200 duration=2i,json_duration=60

View File

@ -0,0 +1,46 @@
{
"message": "abc",
"fields": {
"status": 200,
"key": 1,
"json": [
{
"duration": 100,
"code": 1,
"label": 2,
"line": 3,
"many": 4,
"more": 5,
"numerical": 6,
"fields": 7,
"nest": {
"label": 2,
"line": 3,
"many": 4,
"more": 5,
"numerical": 4,
"fields": 7
}
},
{
"duration": 60,
"code": 1,
"label": 2,
"line": 3,
"many": 4,
"more": 5,
"numerical": 6,
"fields": 7,
"nest": {
"label": 2,
"line": 3,
"many": 4,
"more": 5,
"numerical": 6,
"fields": 7
}
}
],
"duration": 2
}
}

View File

@ -0,0 +1,14 @@
# Example taken from: https://github.com/influxdata/telegraf/issues/1363
[[inputs.file]]
files = ["./testdata/fields_and_tags/input.json"]
data_format = "json_v2"
[[inputs.file.json_v2]]
[[inputs.file.json_v2.tag]]
path = "fields.status"
[[inputs.file.json_v2.field]]
path = "fields.json.#.duration"
rename = "json_duration"
[[inputs.file.json_v2.field]]
path = "fields.duration"
type = "int"

View File

@ -0,0 +1,5 @@
bart_json_v2,name=Powell\ St. minutes=9i
bart_json_v2,name=Powell\ St. minutes=40i
bart_json_v2,name=Powell\ St. minutes=70i
bart_json_v2,name=Powell\ St. minutes=12i
bart_json_v2,name=Powell\ St. minutes=42i

View File

@ -0,0 +1,87 @@
{
"?xml": {
"@version": "1.0",
"@encoding": "utf-8"
},
"root": {
"@id": "1",
"uri": {
"#cdata-section": "http://api.bart.gov/api/etd.aspx?cmd=etd&orig=POWL&json=y"
},
"date": "06/03/2021",
"time": "09:46:01 AM PDT",
"station": [
{
"name": "Powell St.",
"abbr": "POWL",
"etd": [
{
"destination": "Antioch",
"abbreviation": "ANTC",
"limited": "0",
"estimate": [
{
"minutes": "9",
"platform": "2",
"direction": "North",
"length": "10",
"color": "YELLOW",
"hexcolor": "#ffff33",
"bikeflag": "1",
"delay": "0"
},
{
"minutes": "40",
"platform": "2",
"direction": "North",
"length": "10",
"color": "YELLOW",
"hexcolor": "#ffff33",
"bikeflag": "1",
"delay": "0"
},
{
"minutes": "70",
"platform": "2",
"direction": "North",
"length": "10",
"color": "YELLOW",
"hexcolor": "#ffff33",
"bikeflag": "1",
"delay": "0"
}
]
},
{
"destination": "Berryessa",
"abbreviation": "BERY",
"limited": "0",
"estimate": [
{
"minutes": "12",
"platform": "2",
"direction": "North",
"length": "10",
"color": "GREEN",
"hexcolor": "#339933",
"bikeflag": "1",
"delay": "0"
},
{
"minutes": "42",
"platform": "2",
"direction": "North",
"length": "10",
"color": "GREEN",
"hexcolor": "#339933",
"bikeflag": "1",
"delay": "0"
}
]
}
]
}
],
"message": ""
}
}

View File

@ -0,0 +1,10 @@
[[inputs.file]]
files = ["./testdata/fields_and_tags_complex/input.json"]
data_format = "json_v2"
[[inputs.file.json_v2]]
measurement_name = "bart_json_v2"
[[inputs.file.json_v2.tag]]
path = "root.station.#.name"
[[inputs.file.json_v2.field]]
path = "root.station.#.etd.#.estimate.#.minutes"
type = "int"

View File

@ -0,0 +1 @@
32 label="Basic"

View File

@ -0,0 +1,19 @@
{
"value_id": "52-32-1-0",
"node_id": 52,
"class_id": 32,
"type": "byte",
"genre": "basic",
"instance": 1,
"index": 0,
"label": "Basic",
"units": "",
"help": "Basic status of the node",
"read_only": false,
"write_only": false,
"min": 0,
"max": 255,
"is_polled": false,
"value": 0,
"lastUpdate": 1584636017962
}

View File

@ -0,0 +1,9 @@
# Example taken from: https://github.com/influxdata/feature-requests/issues/160
[[inputs.file]]
files = ["./testdata/measurement_name_int/input.json"]
data_format = "json_v2"
[[inputs.file.json_v2]]
measurement_name_path = "class_id"
[[inputs.file.json_v2.field]]
path = "label"

View File

@ -0,0 +1,6 @@
file,title=The\ Lord\ Of\ The\ Rings author="Tolkien",chapters="A Long-expected Party"
file,title=The\ Lord\ Of\ The\ Rings author="Tolkien",chapters="The Shadow of the Past"
file,title=The\ Lord\ Of\ The\ Rings author="Tolkien",name="Bilbo",species="hobbit"
file,title=The\ Lord\ Of\ The\ Rings author="Tolkien",name="Frodo",species="hobbit"
file,title=The\ Lord\ Of\ The\ Rings author="Tolkien",random=1
file,title=The\ Lord\ Of\ The\ Rings author="Tolkien",random=2

View File

@ -0,0 +1,24 @@
{
"book": {
"title": "The Lord Of The Rings",
"chapters": [
"A Long-expected Party",
"The Shadow of the Past"
],
"author": "Tolkien",
"characters": [
{
"name": "Bilbo",
"species": "hobbit"
},
{
"name": "Frodo",
"species": "hobbit"
}
],
"random": [
1,
2
]
}
}

View File

@ -0,0 +1,11 @@
# Example getting nested fields with duplicate names
# Example taken from: https://github.com/influxdata/telegraf/issues/1363
[[inputs.file]]
files = ["./testdata/multiple_arrays_in_object/input.json"]
data_format = "json_v2"
[[inputs.file.json_v2]]
[[inputs.file.json_v2.object]]
path = "book"
tags = ["title"]
disable_prepend_keys = true

View File

@ -0,0 +1,2 @@
file name="fire" 1555745371450794118
file name="flood" 1555745371450794118

View File

@ -0,0 +1,12 @@
{
"events": [
{
"name": "fire",
"time": "1555745371410"
},
{
"name": "flood",
"time": "1555745371410"
}
]
}

View File

@ -0,0 +1,10 @@
# Example taken from: https://github.com/influxdata/telegraf/issues/5940
[[inputs.file]]
files = ["./testdata/multiple_timestamps/input.json"]
data_format = "json_v2"
[[inputs.file.json_v2]]
[[inputs.file.json_v2.object]]
path = "events"
timestamp_key = "time"
timestamp_format = "unix_ms"

View File

@ -0,0 +1,12 @@
file,hostname=testhost1,outputname=1A-CC01-PC01 systemVoltage=-54.1,systemCurrent=-3.8
file,hostname=testhost1,outputname=2A-CC01-KA01 systemVoltage=-54.1,systemCurrent=-3.8
file,hostname=testhost1,outputname=3A-CC01-CC02 systemVoltage=-54.1,systemCurrent=-3.8
file,hostname=testhost1,outputname=4A systemVoltage=-54.1,systemCurrent=-3.8
file,hostname=testhost1,outputname=5A systemVoltage=-54.1,systemCurrent=-3.8
file,hostname=testhost1,outputname=6A-CC01-88-INV01-A systemVoltage=-54.1,systemCurrent=-3.8
file,hostname=testhost2,outputname=1A systemVoltage=27.5,systemCurrent=9.5
file,hostname=testhost2,outputname=2A systemVoltage=27.5,systemCurrent=9.5
file,hostname=testhost2,outputname=3A systemVoltage=27.5,systemCurrent=9.5
file,hostname=testhost2,outputname=4A systemVoltage=27.5,systemCurrent=9.5
file,hostname=testhost2,outputname=5A systemVoltage=27.5,systemCurrent=9.5
file,hostname=testhost2,outputname=6A systemVoltage=27.5,systemCurrent=9.5

View File

@ -0,0 +1,174 @@
[
{
"hostname": "testhost1",
"systemVoltage": -54.1,
"systemCurrent": -3.8,
"tables": [
{
"outputnumber": 0.0,
"outputname": "1A-CC01-PC01",
"outputcurrent": -2.7,
"outputfusestatus": 1.0,
"outputenable": 1.0
},
{
"outputnumber": 1.0,
"outputname": "2A-CC01-KA01",
"outputcurrent": 0.0,
"outputfusestatus": 1.0,
"outputenable": 1.0
},
{
"outputnumber": 2.0,
"outputname": "3A-CC01-CC02",
"outputcurrent": 0.0,
"outputfusestatus": 1.0,
"outputenable": 1.0
},
{
"outputnumber": 3.0,
"outputname": "4A",
"outputcurrent": 0.0,
"outputfusestatus": 1.0,
"outputenable": 1.0
},
{
"outputnumber": 4.0,
"outputname": "5A",
"outputcurrent": 0.0,
"outputfusestatus": 1.0,
"outputenable": 1.0
},
{
"outputnumber": 5.0,
"outputname": "6A-CC01-88-INV01-A",
"outputcurrent": -1.1,
"outputfusestatus": 1.0,
"outputenable": 1.0
},
{
"busnumber": 0.0,
"busname": "A--48A",
"busvoltage": -54.1,
"buscurrent": -3.8
},
{
"busnumber": 1.0,
"busname": "B--48B",
"busvoltage": -53.9,
"buscurrent": -4.2
},
{
"alarmnumber": 0.0,
"alarmname": "\u0000",
"alarmstatus": 1.0
},
{
"alarmnumber": 1.0,
"alarmname": "\u0000",
"alarmstatus": 1.0
},
{
"alarmnumber": 2.0,
"alarmname": "\u0000",
"alarmstatus": 1.0
},
{
"alarmnumber": 3.0,
"alarmname": "\u0000",
"alarmstatus": 1.0
},
{
"alarmnumber": 4.0,
"alarmname": "\u0000",
"alarmstatus": 1.0
}
]
},
{
"hostname": "testhost2",
"systemVoltage": 27.5,
"systemCurrent": 9.5,
"tables": [
{
"outputnumber": 0.0,
"outputname": "1A",
"outputcurrent": 0.0,
"outputfusestatus": 1.0,
"outputenable": 1.0
},
{
"outputnumber": 1.0,
"outputname": "2A",
"outputcurrent": 0.0,
"outputfusestatus": 1.0,
"outputenable": 1.0
},
{
"outputnumber": 2.0,
"outputname": "3A",
"outputcurrent": 0.0,
"outputfusestatus": 1.0,
"outputenable": 1.0
},
{
"outputnumber": 3.0,
"outputname": "4A",
"outputcurrent": 0.6,
"outputfusestatus": 1.0,
"outputenable": 1.0
},
{
"outputnumber": 4.0,
"outputname": "5A",
"outputcurrent": 6.5,
"outputfusestatus": 1.0,
"outputenable": 1.0
},
{
"outputnumber": 5.0,
"outputname": "6A",
"outputcurrent": 0.0,
"outputfusestatus": 2.0,
"outputenable": 1.0
},
{
"busnumber": 0.0,
"busname": "A-24V",
"busvoltage": 27.6,
"buscurrent": 0.6
},
{
"busnumber": 1.0,
"busname": "B-12V",
"busvoltage": 13.8,
"buscurrent": 0.0
},
{
"alarmnumber": 0.0,
"alarmname": "\u0000",
"alarmstatus": 1.0
},
{
"alarmnumber": 1.0,
"alarmname": "\u0000",
"alarmstatus": 1.0
},
{
"alarmnumber": 2.0,
"alarmname": "\u0000",
"alarmstatus": 1.0
},
{
"alarmnumber": 3.0,
"alarmname": "\u0000",
"alarmstatus": 1.0
},
{
"alarmnumber": 4.0,
"alarmname": "\u0000",
"alarmstatus": 1.0
}
]
}
]

View File

@ -0,0 +1,18 @@
# Example taken from: https://github.com/influxdata/telegraf/issues/6437
# Parse String types from JSON
[[inputs.file]]
files = ["./testdata/nested_and_nonnested_tags/input.json"]
data_format = "json_v2"
[[inputs.file.json_v2]]
[[inputs.file.json_v2.object]]
disable_prepend_keys = true
path = "@this"
included_keys = [
"hostname",
"systemVoltage",
"systemCurrent",
"tables",
"tables_outputname",
]
tags = ["hostname", "tables_outputname"]

View File

@ -0,0 +1,2 @@
new_metric,name=partition LogEndOffset=339238i,LogStartOffset=339238i,NumLogSegments=1i,Size=0i,UnderReplicatedPartitions=0i 1610056029037925000
new_metric,name=partition LogEndOffset=33914i,LogStartOffset=33238i,NumLogSegments=1i,Size=2i,UnderReplicatedPartitions=5i 1610056029037956000

View File

@ -0,0 +1,36 @@
[
{
"data": {
"LogEndOffset": 339238,
"LogStartOffset": 339238,
"NumLogSegments": 1,
"Size": 0,
"UnderReplicatedPartitions": 0
},
"name": "partition",
"tags": {
"host": "CUD1-001559",
"jolokia_agent_url": "http://localhost:7777/jolokia",
"partition": "1",
"topic": "qa-kafka-connect-logs"
},
"timestamp": 1591124461
},
{
"data": {
"LogEndOffset": 33914,
"LogStartOffset": 33238,
"NumLogSegments": 1,
"Size": 2,
"UnderReplicatedPartitions": 5
},
"name": "partition",
"tags": {
"host": "CUD1-001559",
"jolokia_agent_url": "http://localhost:7777/jolokia",
"partition": "1",
"topic": "qa-kafka-connect-logs"
},
"timestamp": 1591124461
}
]

View File

@ -0,0 +1,15 @@
# Example taken from: https://github.com/influxdata/feature-requests/issues/160
[[inputs.file]]
files = ["./testdata/nested_array_of_objects/input.json"]
data_format = "json_v2"
[[inputs.file.json_v2]]
measurement_name = "new_metric"
[[inputs.file.json_v2.object]]
path = "@this"
disable_prepend_keys = true
excluded_keys = ["tags", "timestamp"]
tags = ["name"]
[inputs.file.json_v2.object.fields]
data = "int"

View File

@ -0,0 +1,2 @@
file,Firmware=LDGSW07G,Model=WDC\ WUH721414ALE604,Serial=9JHNGTUT Count=0,Errors=0
file,Firmware=LDGSW07G,Model=WDC\ WUH721414ALE604,Serial=9JHLPW9T Errors=0,Count=0

View File

@ -0,0 +1,16 @@
{
"device0": {
"Count": 0,
"Errors": 0,
"Serial": "9JHNGTUT",
"Model": "WDC WUH721414ALE604",
"Firmware": "LDGSW07G"
},
"device1": {
"Count": 0,
"Errors": 0,
"Serial": "9JHLPW9T",
"Model": "WDC WUH721414ALE604",
"Firmware": "LDGSW07G"
}
}

View File

@ -0,0 +1,12 @@
# Example taken from: https://github.com/influxdata/telegraf/issues/6853
[[inputs.file]]
files = ["./testdata/nested_tags/input.json"]
data_format = "json_v2"
[[inputs.file.json_v2]]
[[inputs.file.json_v2.object]]
path = "device0"
tags = ["Firmware", "Model", "Serial"]
[[inputs.file.json_v2.object]]
path = "device1"
tags = ["Firmware", "Model", "Serial"]

View File

@ -0,0 +1,3 @@
file,Firmware=LDGSW07G,Model=WDC\ WUH721414ALE604,Serial=9JHNGTUT Count=0,Errors=0
file,Firmware=LDGSW07G,Model=WDC\ WUH721414ALE604,Serial=9JHNGHJBT Errors=0,Count=2
file,Firmware=LDGSW07G,Model=WDC\ WUH721414ALE604,Serial=9JHLPW9T Errors=0,Count=0

View File

@ -0,0 +1,35 @@
{
"Group A": [
{
"Sub-group 1": [
{
"Count": 0,
"Errors": 0,
"Serial": "9JHNGTUT",
"Model": "WDC WUH721414ALE604",
"Firmware": "LDGSW07G"
},
{
"Count": 2,
"Errors": 0,
"Serial": "9JHNGHJBT",
"Model": "WDC WUH721414ALE604",
"Firmware": "LDGSW07G"
}
]
}
],
"Group B": [
{
"Sub-group 1": [
{
"Count": 0,
"Errors": 0,
"Serial": "9JHLPW9T",
"Model": "WDC WUH721414ALE604",
"Firmware": "LDGSW07G"
}
]
}
]
}

View File

@ -0,0 +1,14 @@
# Example taken from: https://github.com/influxdata/telegraf/issues/6853
[[inputs.file]]
files = ["./testdata/nested_tags_complex/input.json"]
data_format = "json_v2"
[[inputs.file.json_v2]]
[[inputs.file.json_v2.object]]
path = "Group A"
disable_prepend_keys = true
tags = ["Sub-group_1_Firmware", "Sub-group_1_Model", "Sub-group_1_Serial"]
[[inputs.file.json_v2.object]]
path = "Group B"
disable_prepend_keys = true
tags = ["Sub-group_1_Firmware", "Sub-group_1_Model", "Sub-group_1_Serial"]

View File

@ -0,0 +1,5 @@
bart_json_v2,destination=Antioch,name=Colma minutes=13i
bart_json_v2,destination=Antioch,name=Colma minutes=43i
bart_json_v2,destination=Millbrae,name=Colma minutes=19i
bart_json_v2,destination=Millbrae,name=Colma minutes=49i
bart_json_v2,destination=Millbrae,name=Colma minutes=79i

View File

@ -0,0 +1,87 @@
{
"?xml": {
"@version": "1.0",
"@encoding": "utf-8"
},
"root": {
"@id": "1",
"uri": {
"#cdata-section": "http://api.bart.gov/api/etd.aspx?cmd=etd&orig=COLM&json=y"
},
"date": "06/03/2021",
"time": "12:54:31 PM PDT",
"station": [
{
"name": "Colma",
"abbr": "COLM",
"etd": [
{
"destination": "Antioch",
"abbreviation": "ANTC",
"limited": "0",
"estimate": [
{
"minutes": "13",
"platform": "2",
"direction": "North",
"length": "10",
"color": "YELLOW",
"hexcolor": "#ffff33",
"bikeflag": "1",
"delay": "0"
},
{
"minutes": "43",
"platform": "2",
"direction": "North",
"length": "10",
"color": "YELLOW",
"hexcolor": "#ffff33",
"bikeflag": "1",
"delay": "0"
}
]
},
{
"destination": "Millbrae",
"abbreviation": "MLBR",
"limited": "0",
"estimate": [
{
"minutes": "19",
"platform": "1",
"direction": "South",
"length": "10",
"color": "RED",
"hexcolor": "#ff0000",
"bikeflag": "1",
"delay": "0"
},
{
"minutes": "49",
"platform": "1",
"direction": "South",
"length": "10",
"color": "RED",
"hexcolor": "#ff0000",
"bikeflag": "1",
"delay": "0"
},
{
"minutes": "79",
"platform": "1",
"direction": "South",
"length": "10",
"color": "RED",
"hexcolor": "#ff0000",
"bikeflag": "1",
"delay": "0"
}
]
}
]
}
],
"message": ""
}
}

View File

@ -0,0 +1,12 @@
[[inputs.file]]
files = ["./testdata/object/input.json"]
data_format = "json_v2"
[[inputs.file.json_v2]]
measurement_name = "bart_json_v2"
[[inputs.file.json_v2.object]]
path = "root.station"
disable_prepend_keys = true
included_keys = ["name", "etd_destination", "etd_estimate_minutes"]
tags = ["name", "etd_destination"]
[inputs.file.json_v2.object.fields]
etd_estimate_minutes = "int"

View File

@ -0,0 +1,4 @@
file,name=temperature,units=℃ value=23.4 1555745371450794118
file,name=moisture,units=% value=5 1555745371450794118
file,name=light,units=lux value=10118 1555745371450794118
file,name=fertility,units=us/cm value=0 1555745371450794118

View File

@ -0,0 +1,25 @@
{
"time": 1555745371410,
"measurements": [
{
"name": "temperature",
"value": 23.4,
"units": "℃"
},
{
"name": "moisture",
"value": 5,
"units": "%"
},
{
"name": "light",
"value": 10118,
"units": "lux"
},
{
"name": "fertility",
"value": 0,
"units": "us/cm"
}
]
}

View File

@ -0,0 +1,11 @@
# Example taken from: https://github.com/influxdata/telegraf/issues/5940
[[inputs.file]]
files = ["./testdata/timestamp/input.json"]
data_format = "json_v2"
[[inputs.file.json_v2]]
timestamp_path = "time"
timestamp_format = "unix_ms"
[[inputs.file.json_v2.object]]
path = "measurements"
tags = ["name", "units"]

View File

@ -0,0 +1,4 @@
file explicitstringtypeName="Bilbo",defaultstringtypeName="Baggins",convertbooltostringName="true",convertinttostringName="1",convertfloattostringName="1.1"
file defaultinttypeName=2,convertfloatointName=3i,convertstringtointName=4i,convertbooltointName=0i,explicitinttypeName=1i,uinttype=1u
file convertstringtofloatName=4.1,explicitfloattypeName=1.1,defaultfloattypeName=2.1,convertintotfloatName=3
file explicitbooltypeName=true,defaultbooltypeName=false,convertinttoboolName=true,convertstringtoboolName=false,convertintstringtoboolTrueName=true,convertintstringtoboolFalseName=false

View File

@ -0,0 +1,22 @@
{
"explicitstringtype": "Bilbo",
"defaultstringtype": "Baggins",
"convertbooltostring": true,
"convertinttostring": 1,
"convertfloattostring": 1.1,
"explicitinttype": 1,
"defaultinttype": 2,
"convertfloatoint": 3.1,
"convertstringtoint": "4",
"convertbooltoint": false,
"explicitfloattype": 1.1,
"defaultfloattype": 2.1,
"convertintotfloat": 3,
"convertstringtofloat": "4.1",
"explicitbooltype": true,
"defaultbooltype": false,
"convertinttobool": 1,
"convertstringtobool": "false",
"convertintstringtoboolTrue": "1",
"convertintstringtoboolFalse": "0"
}

View File

@ -0,0 +1,105 @@
# Example taken from: https://github.com/influxdata/telegraf/issues/7097
# Parse String types from JSON
[[inputs.file]]
files = ["./testdata/types/input.json"]
data_format = "json_v2"
[[inputs.file.json_v2]]
[[inputs.file.json_v2.field]]
rename = "explicitstringtypeName"
path = "explicitstringtype"
type = "string"
[[inputs.file.json_v2.field]]
rename = "defaultstringtypeName"
path = "defaultstringtype"
[[inputs.file.json_v2.field]]
rename = "convertbooltostringName"
path = "convertbooltostring"
type = "string"
[[inputs.file.json_v2.field]]
rename = "convertinttostringName"
path = "convertinttostring"
type = "string"
[[inputs.file.json_v2.field]]
rename = "convertfloattostringName"
path = "convertfloattostring"
type = "string"
# Parse int typess from JSON
[[inputs.file]]
files = ["./testdata/types/input.json"]
data_format = "json_v2"
[[inputs.file.json_v2]]
[[inputs.file.json_v2.field]]
rename = "explicitinttypeName"
path = "explicitinttype"
type = "int"
[[inputs.file.json_v2.field]]
rename = "uinttype"
path = "explicitinttype"
type = "uint"
[[inputs.file.json_v2.field]]
rename = "defaultinttypeName"
path = "defaultinttype"
[[inputs.file.json_v2.field]]
rename = "convertfloatointName"
path = "convertfloatoint"
type = "int"
[[inputs.file.json_v2.field]]
rename = "convertstringtointName"
path = "convertstringtoint"
type = "int"
[[inputs.file.json_v2.field]]
rename = "convertbooltointName"
path = "convertbooltoint"
type = "int"
# Parse float types from JSON
[[inputs.file]]
files = ["./testdata/types/input.json"]
data_format = "json_v2"
[[inputs.file.json_v2]]
[[inputs.file.json_v2.field]]
rename = "explicitfloattypeName"
path = "explicitfloattype"
type = "float"
[[inputs.file.json_v2.field]]
rename = "defaultfloattypeName"
path = "defaultfloattype"
[[inputs.file.json_v2.field]]
rename = "convertintotfloatName"
path = "convertintotfloat"
type = "float"
[[inputs.file.json_v2.field]]
rename = "convertstringtofloatName"
path = "convertstringtofloat"
type = "float"
# Parse bool types from JSON
[[inputs.file]]
files = ["./testdata/types/input.json"]
data_format = "json_v2"
[[inputs.file.json_v2]]
[[inputs.file.json_v2.field]]
rename = "explicitbooltypeName"
path = "explicitbooltype"
type = "bool"
[[inputs.file.json_v2.field]]
rename = "defaultbooltypeName"
path = "defaultbooltype"
[[inputs.file.json_v2.field]]
rename = "convertinttoboolName"
path = "convertinttobool"
type = "bool"
[[inputs.file.json_v2.field]]
rename = "convertstringtoboolName"
path = "convertstringtobool"
type = "bool"
[[inputs.file.json_v2.field]]
rename = "convertintstringtoboolTrueName"
path = "convertintstringtoboolTrue"
type = "bool"
[[inputs.file.json_v2.field]]
rename = "convertintstringtoboolFalseName"
path = "convertintstringtoboolFalse"
type = "bool"

View File

@ -12,6 +12,7 @@ import (
"github.com/influxdata/telegraf/plugins/parsers/grok"
"github.com/influxdata/telegraf/plugins/parsers/influx"
"github.com/influxdata/telegraf/plugins/parsers/json"
"github.com/influxdata/telegraf/plugins/parsers/json_v2"
"github.com/influxdata/telegraf/plugins/parsers/logfmt"
"github.com/influxdata/telegraf/plugins/parsers/nagios"
"github.com/influxdata/telegraf/plugins/parsers/prometheus"
@ -51,6 +52,8 @@ type Parser interface {
// and parses it into a telegraf metric.
//
// Must be thread-safe.
// This function is only called by plugins that expect line based protocols
// Doesn't need to be implemented by non-linebased parsers (e.g. json, xml)
ParseLine(line string) (telegraf.Metric, error)
// SetDefaultTags tells the parser to add all of the given tags
@ -158,12 +161,19 @@ type Config struct {
// XML configuration
XMLConfig []XMLConfig `toml:"xml"`
// JSONPath configuration
JSONV2Config []JSONV2Config `toml:"json_v2"`
}
type XMLConfig struct {
xml.Config
}
type JSONV2Config struct {
json_v2.Config
}
// NewParser returns a Parser interface based on the given config.
func NewParser(config *Config) (Parser, error) {
var err error
@ -253,6 +263,8 @@ func NewParser(config *Config) (Parser, error) {
parser, err = NewPrometheusRemoteWriteParser(config.DefaultTags)
case "xml":
parser, err = NewXMLParser(config.MetricName, config.DefaultTags, config.XMLConfig)
case "json_v2":
parser, err = NewJSONPathParser(config.JSONV2Config)
default:
err = fmt.Errorf("Invalid data format: %s", config.DataFormat)
}
@ -395,3 +407,23 @@ func NewXMLParser(metricName string, defaultTags map[string]string, xmlConfigs [
DefaultTags: defaultTags,
}, nil
}
func NewJSONPathParser(jsonv2config []JSONV2Config) (Parser, error) {
configs := make([]json_v2.Config, len(jsonv2config))
for i, cfg := range jsonv2config {
configs[i].MeasurementName = cfg.MeasurementName
configs[i].MeasurementNamePath = cfg.MeasurementNamePath
configs[i].TimestampPath = cfg.TimestampPath
configs[i].TimestampFormat = cfg.TimestampFormat
configs[i].TimestampTimezone = cfg.TimestampTimezone
configs[i].Fields = cfg.Fields
configs[i].Tags = cfg.Tags
configs[i].JSONObjects = cfg.JSONObjects
}
return &json_v2.Parser{
Configs: configs,
}, nil
}