From 373839a594b0fa6e3bbb190ce082c1d7564f7ea2 Mon Sep 17 00:00:00 2001 From: tjmcs Date: Wed, 29 Mar 2017 17:12:29 -0700 Subject: [PATCH] Adds a new json_timestamp_units configuration parameter (#2587) --- docs/DATA_FORMATS_OUTPUT.md | 10 ++++++++++ internal/config/config.go | 20 +++++++++++++++++++- plugins/serializers/json/json.go | 10 +++++++++- plugins/serializers/registry.go | 12 ++++++++---- 4 files changed, 46 insertions(+), 6 deletions(-) diff --git a/docs/DATA_FORMATS_OUTPUT.md b/docs/DATA_FORMATS_OUTPUT.md index 177734d16..633460846 100644 --- a/docs/DATA_FORMATS_OUTPUT.md +++ b/docs/DATA_FORMATS_OUTPUT.md @@ -147,4 +147,14 @@ The JSON data format serialized Telegraf metrics in json format. The format is: ## more about them here: ## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_OUTPUT.md data_format = "json" + json_timestamp_units = "1ns" ``` + +By default, the timestamp that is output in JSON data format serialized Telegraf +metrics is in seconds. The precision of this timestamp can be adjusted for any output +by adding the optional `json_timestamp_units` parameter to the configuration for +that output. This parameter can be used to set the timestamp units to nanoseconds (`ns`), +microseconds (`us` or `µs`), milliseconds (`ms`), or seconds (`s`). Note that this +parameter will be truncated to the nearest power of 10 that, so if the `json_timestamp_units` +are set to `15ms` the timestamps for the JSON format serialized Telegraf metrics will be +output in hundredths of a second (`10ms`). diff --git a/internal/config/config.go b/internal/config/config.go index 651c4e9ef..013e81c12 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -6,6 +6,7 @@ import ( "fmt" "io/ioutil" "log" + "math" "os" "path/filepath" "regexp" @@ -1244,7 +1245,7 @@ func buildParser(name string, tbl *ast.Table) (parsers.Parser, error) { // a serializers.Serializer object, and creates it, which can then be added onto // an Output object. func buildSerializer(name string, tbl *ast.Table) (serializers.Serializer, error) { - c := &serializers.Config{} + c := &serializers.Config{TimestampUnits: time.Duration(1 * time.Second)} if node, ok := tbl.Fields["data_format"]; ok { if kv, ok := node.(*ast.KeyValue); ok { @@ -1274,9 +1275,26 @@ func buildSerializer(name string, tbl *ast.Table) (serializers.Serializer, error } } + if node, ok := tbl.Fields["json_timestamp_units"]; ok { + if kv, ok := node.(*ast.KeyValue); ok { + if str, ok := kv.Value.(*ast.String); ok { + timestampVal, err := time.ParseDuration(str.Value) + if err != nil { + return nil, fmt.Errorf("Unable to parse json_timestamp_units as a duration, %s", err) + } + // now that we have a duration, truncate it to the nearest + // power of ten (just in case) + nearest_exponent := int64(math.Log10(float64(timestampVal.Nanoseconds()))) + new_nanoseconds := int64(math.Pow(10.0, float64(nearest_exponent))) + c.TimestampUnits = time.Duration(new_nanoseconds) + } + } + } + delete(tbl.Fields, "data_format") delete(tbl.Fields, "prefix") delete(tbl.Fields, "template") + delete(tbl.Fields, "json_timestamp_units") return serializers.NewSerializer(c) } diff --git a/plugins/serializers/json/json.go b/plugins/serializers/json/json.go index 3e259fafd..452364c95 100644 --- a/plugins/serializers/json/json.go +++ b/plugins/serializers/json/json.go @@ -2,19 +2,27 @@ package json import ( ejson "encoding/json" + "time" "github.com/influxdata/telegraf" ) type JsonSerializer struct { + TimestampUnits time.Duration } func (s *JsonSerializer) Serialize(metric telegraf.Metric) ([]byte, error) { m := make(map[string]interface{}) + units_nanoseconds := s.TimestampUnits.Nanoseconds() + // if the units passed in were less than or equal to zero, + // then serialize the timestamp in seconds (the default) + if units_nanoseconds <= 0 { + units_nanoseconds = 1000000000 + } m["tags"] = metric.Tags() m["fields"] = metric.Fields() m["name"] = metric.Name() - m["timestamp"] = metric.UnixNano() / 1000000000 + m["timestamp"] = metric.UnixNano() / units_nanoseconds serialized, err := ejson.Marshal(m) if err != nil { return []byte{}, err diff --git a/plugins/serializers/registry.go b/plugins/serializers/registry.go index cb1e03b46..368f6f449 100644 --- a/plugins/serializers/registry.go +++ b/plugins/serializers/registry.go @@ -2,6 +2,7 @@ package serializers import ( "fmt" + "time" "github.com/influxdata/telegraf" @@ -29,7 +30,7 @@ type Serializer interface { // Config is a struct that covers the data types needed for all serializer types, // and can be used to instantiate _any_ of the serializers. type Config struct { - // Dataformat can be one of: influx, graphite + // Dataformat can be one of: influx, graphite, or json DataFormat string // Prefix to add to all measurements, only supports Graphite @@ -38,6 +39,9 @@ type Config struct { // Template for converting telegraf metrics into Graphite // only supports Graphite Template string + + // Timestamp units to use for JSON formatted output + TimestampUnits time.Duration } // NewSerializer a Serializer interface based on the given config. @@ -50,15 +54,15 @@ func NewSerializer(config *Config) (Serializer, error) { case "graphite": serializer, err = NewGraphiteSerializer(config.Prefix, config.Template) case "json": - serializer, err = NewJsonSerializer() + serializer, err = NewJsonSerializer(config.TimestampUnits) default: err = fmt.Errorf("Invalid data format: %s", config.DataFormat) } return serializer, err } -func NewJsonSerializer() (Serializer, error) { - return &json.JsonSerializer{}, nil +func NewJsonSerializer(timestampUnits time.Duration) (Serializer, error) { + return &json.JsonSerializer{TimestampUnits: timestampUnits}, nil } func NewInfluxSerializer() (Serializer, error) {