Add field creation to date processor and integer unix time support (#7464)

This commit is contained in:
Rich Y 2020-05-08 01:19:03 +01:00 committed by GitHub
parent f14b3759d4
commit f076b6c115
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 151 additions and 27 deletions

View File

@ -16,15 +16,23 @@ A few example usecases include:
## New tag to create ## New tag to create
tag_key = "month" tag_key = "month"
## New field to create (cannot set both field_key and tag_key)
# field_key = "month"
## Date format string, must be a representation of the Go "reference time" ## Date format string, must be a representation of the Go "reference time"
## which is "Mon Jan 2 15:04:05 -0700 MST 2006". ## which is "Mon Jan 2 15:04:05 -0700 MST 2006".
date_format = "Jan" date_format = "Jan"
## If destination is a field, date format can also be one of
## "unix", "unix_ms", "unix_us", or "unix_ns", which will insert an integer field.
# date_format = "unix"
## Offset duration added to the date string when writing the new tag. ## Offset duration added to the date string when writing the new tag.
# date_offset = "0s" # date_offset = "0s"
## Timezone to use when generating the date. This can be set to one of ## Timezone to use when creating the tag or field using a reference time
## "Local", "UTC", or to a location name in the IANA Time Zone database. ## string. This can be set to one of "UTC", "Local", or to a location name
## in the IANA Time Zone database.
## example: timezone = "America/Los_Angeles" ## example: timezone = "America/Los_Angeles"
# timezone = "UTC" # timezone = "UTC"
``` ```

View File

@ -1,6 +1,7 @@
package date package date
import ( import (
"errors"
"time" "time"
"github.com/influxdata/telegraf" "github.com/influxdata/telegraf"
@ -9,26 +10,35 @@ import (
) )
const sampleConfig = ` const sampleConfig = `
## New tag to create ## New tag to create
tag_key = "month" tag_key = "month"
## Date format string, must be a representation of the Go "reference time" ## New field to create (cannot set both field_key and tag_key)
## which is "Mon Jan 2 15:04:05 -0700 MST 2006". # field_key = "month"
date_format = "Jan"
## Offset duration added to the date string when writing the new tag. ## Date format string, must be a representation of the Go "reference time"
# date_offset = "0s" ## which is "Mon Jan 2 15:04:05 -0700 MST 2006".
date_format = "Jan"
## Timezone to use when creating the tag. This can be set to one of ## If destination is a field, date format can also be one of
## "UTC", "Local", or to a location name in the IANA Time Zone database. ## "unix", "unix_ms", "unix_us", or "unix_ns", which will insert an integer field.
## example: timezone = "America/Los_Angeles" # date_format = "unix"
# timezone = "UTC"
## Offset duration added to the date string when writing the new tag.
# date_offset = "0s"
## Timezone to use when creating the tag or field using a reference time
## string. This can be set to one of "UTC", "Local", or to a location name
## in the IANA Time Zone database.
## example: timezone = "America/Los_Angeles"
# timezone = "UTC"
` `
const defaultTimezone = "UTC" const defaultTimezone = "UTC"
type Date struct { type Date struct {
TagKey string `toml:"tag_key"` TagKey string `toml:"tag_key"`
FieldKey string `toml:"field_key"`
DateFormat string `toml:"date_format"` DateFormat string `toml:"date_format"`
DateOffset internal.Duration `toml:"date_offset"` DateOffset internal.Duration `toml:"date_offset"`
Timezone string `toml:"timezone"` Timezone string `toml:"timezone"`
@ -45,6 +55,13 @@ func (d *Date) Description() string {
} }
func (d *Date) Init() error { func (d *Date) Init() error {
// Check either TagKey or FieldKey specified
if len(d.FieldKey) > 0 && len(d.TagKey) > 0 {
return errors.New("Only one of field_key or tag_key can be specified")
} else if len(d.FieldKey) == 0 && len(d.TagKey) == 0 {
return errors.New("One of field_key or tag_key must be specified")
}
var err error var err error
// LoadLocation returns UTC if timezone is the empty string. // LoadLocation returns UTC if timezone is the empty string.
d.location, err = time.LoadLocation(d.Timezone) d.location, err = time.LoadLocation(d.Timezone)
@ -54,7 +71,22 @@ func (d *Date) Init() error {
func (d *Date) Apply(in ...telegraf.Metric) []telegraf.Metric { func (d *Date) Apply(in ...telegraf.Metric) []telegraf.Metric {
for _, point := range in { for _, point := range in {
tm := point.Time().In(d.location).Add(d.DateOffset.Duration) tm := point.Time().In(d.location).Add(d.DateOffset.Duration)
point.AddTag(d.TagKey, tm.Format(d.DateFormat)) if len(d.TagKey) > 0 {
point.AddTag(d.TagKey, tm.Format(d.DateFormat))
} else if len(d.FieldKey) > 0 {
switch d.DateFormat {
case "unix":
point.AddField(d.FieldKey, tm.Unix())
case "unix_ms":
point.AddField(d.FieldKey, tm.UnixNano()/1000000)
case "unix_us":
point.AddField(d.FieldKey, tm.UnixNano()/1000)
case "unix_ns":
point.AddField(d.FieldKey, tm.UnixNano())
default:
point.AddField(d.FieldKey, tm.Format(d.DateFormat))
}
}
} }
return in return in

View File

@ -23,6 +23,22 @@ func MustMetric(name string, tags map[string]string, fields map[string]interface
return m return m
} }
func TestTagAndField(t *testing.T) {
dateFormatTagAndField := Date{
TagKey: "month",
FieldKey: "month",
}
err := dateFormatTagAndField.Init()
require.Error(t, err)
}
func TestNoOutputSpecified(t *testing.T) {
dateFormatNoOutput := Date{}
err := dateFormatNoOutput.Init()
require.Error(t, err)
}
func TestMonthTag(t *testing.T) { func TestMonthTag(t *testing.T) {
dateFormatMonth := Date{ dateFormatMonth := Date{
TagKey: "month", TagKey: "month",
@ -43,25 +59,25 @@ func TestMonthTag(t *testing.T) {
assert.Equal(t, map[string]string{"month": month}, monthApply[2].Tags(), "should add tag 'month'") assert.Equal(t, map[string]string{"month": month}, monthApply[2].Tags(), "should add tag 'month'")
} }
func TestYearTag(t *testing.T) { func TestMonthField(t *testing.T) {
dateFormatYear := Date{ dateFormatMonth := Date{
TagKey: "year", FieldKey: "month",
DateFormat: "2006", DateFormat: "Jan",
} }
err := dateFormatYear.Init() err := dateFormatMonth.Init()
require.NoError(t, err) require.NoError(t, err)
currentTime := time.Now() currentTime := time.Now()
year := currentTime.Format("2006") month := currentTime.Format("Jan")
m4 := MustMetric("foo", nil, nil, currentTime) m1 := MustMetric("foo", nil, nil, currentTime)
m5 := MustMetric("bar", nil, nil, currentTime) m2 := MustMetric("bar", nil, nil, currentTime)
m6 := MustMetric("baz", nil, nil, currentTime) m3 := MustMetric("baz", nil, nil, currentTime)
yearApply := dateFormatYear.Apply(m4, m5, m6) monthApply := dateFormatMonth.Apply(m1, m2, m3)
assert.Equal(t, map[string]string{"year": year}, yearApply[0].Tags(), "should add tag 'year'") assert.Equal(t, map[string]interface{}{"month": month}, monthApply[0].Fields(), "should add field 'month'")
assert.Equal(t, map[string]string{"year": year}, yearApply[1].Tags(), "should add tag 'year'") assert.Equal(t, map[string]interface{}{"month": month}, monthApply[1].Fields(), "should add field 'month'")
assert.Equal(t, map[string]string{"year": year}, yearApply[2].Tags(), "should add tag 'year'") assert.Equal(t, map[string]interface{}{"month": month}, monthApply[2].Fields(), "should add field 'month'")
} }
func TestOldDateTag(t *testing.T) { func TestOldDateTag(t *testing.T) {
@ -78,6 +94,74 @@ func TestOldDateTag(t *testing.T) {
assert.Equal(t, map[string]string{"year": "1993"}, customDateApply[0].Tags(), "should add tag 'year'") assert.Equal(t, map[string]string{"year": "1993"}, customDateApply[0].Tags(), "should add tag 'year'")
} }
func TestFieldUnix(t *testing.T) {
dateFormatUnix := Date{
FieldKey: "unix",
DateFormat: "unix",
}
err := dateFormatUnix.Init()
require.NoError(t, err)
currentTime := time.Now()
unixTime := currentTime.Unix()
m8 := MustMetric("foo", nil, nil, currentTime)
unixApply := dateFormatUnix.Apply(m8)
assert.Equal(t, map[string]interface{}{"unix": unixTime}, unixApply[0].Fields(), "should add unix time in s as field 'unix'")
}
func TestFieldUnixNano(t *testing.T) {
dateFormatUnixNano := Date{
FieldKey: "unix_ns",
DateFormat: "unix_ns",
}
err := dateFormatUnixNano.Init()
require.NoError(t, err)
currentTime := time.Now()
unixNanoTime := currentTime.UnixNano()
m9 := MustMetric("foo", nil, nil, currentTime)
unixNanoApply := dateFormatUnixNano.Apply(m9)
assert.Equal(t, map[string]interface{}{"unix_ns": unixNanoTime}, unixNanoApply[0].Fields(), "should add unix time in ns as field 'unix_ns'")
}
func TestFieldUnixMillis(t *testing.T) {
dateFormatUnixMillis := Date{
FieldKey: "unix_ms",
DateFormat: "unix_ms",
}
err := dateFormatUnixMillis.Init()
require.NoError(t, err)
currentTime := time.Now()
unixMillisTime := currentTime.UnixNano() / 1000000
m10 := MustMetric("foo", nil, nil, currentTime)
unixMillisApply := dateFormatUnixMillis.Apply(m10)
assert.Equal(t, map[string]interface{}{"unix_ms": unixMillisTime}, unixMillisApply[0].Fields(), "should add unix time in ms as field 'unix_ms'")
}
func TestFieldUnixMicros(t *testing.T) {
dateFormatUnixMicros := Date{
FieldKey: "unix_us",
DateFormat: "unix_us",
}
err := dateFormatUnixMicros.Init()
require.NoError(t, err)
currentTime := time.Now()
unixMicrosTime := currentTime.UnixNano() / 1000
m11 := MustMetric("foo", nil, nil, currentTime)
unixMicrosApply := dateFormatUnixMicros.Apply(m11)
assert.Equal(t, map[string]interface{}{"unix_us": unixMicrosTime}, unixMicrosApply[0].Fields(), "should add unix time in us as field 'unix_us'")
}
func TestDateOffset(t *testing.T) { func TestDateOffset(t *testing.T) {
plugin := &Date{ plugin := &Date{
TagKey: "hour", TagKey: "hour",