diff --git a/CHANGELOG.md b/CHANGELOG.md index e7ec20ca7..7cee28b02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ renamed to `sqlserver_azure_db_resource_stats` due to an issue where numeric metrics were previously being reported incorrectly as strings. +- The `date` processor now uses the UTC timezone when creating its tag. In + previous versions the local time was used. + #### New Outputs - [warp10](/plugins/outputs/warp10/README.md) - Contributed by @aurrelhebert diff --git a/plugins/processors/date/README.md b/plugins/processors/date/README.md index 1a68119e1..b04964b4a 100644 --- a/plugins/processors/date/README.md +++ b/plugins/processors/date/README.md @@ -19,6 +19,23 @@ A few example usecases include: ## Date format string, must be a representation of the Go "reference time" ## which is "Mon Jan 2 15:04:05 -0700 MST 2006". date_format = "Jan" + + ## Offset duration added to the date string when writing the new tag. + # date_offset = "0s" + + ## Timezone to use when generating the date. This can be set to one of + ## "Local", "UTC", or to a location name in the IANA Time Zone database. + ## example: timezone = "America/Los_Angeles" + # timezone = "UTC" +``` + +#### timezone + +On Windows, only the `Local` and `UTC` zones are available by default. To use +other timezones, set the `ZONEINFO` environment variable to the location of +[`zoneinfo.zip`][zoneinfo]: +``` +set ZONEINFO=C:\zoneinfo.zip ``` ### Example @@ -27,3 +44,5 @@ A few example usecases include: - throughput lower=10i,upper=1000i,mean=500i 1560540094000000000 + throughput,month=Jun lower=10i,upper=1000i,mean=500i 1560540094000000000 ``` + +[zoneinfo]: https://github.com/golang/go/raw/50bd1c4d4eb4fac8ddeb5f063c099daccfb71b26/lib/time/zoneinfo.zip diff --git a/plugins/processors/date/date.go b/plugins/processors/date/date.go index 479106ef2..c8007323f 100644 --- a/plugins/processors/date/date.go +++ b/plugins/processors/date/date.go @@ -1,7 +1,10 @@ package date import ( + "time" + "github.com/influxdata/telegraf" + "github.com/influxdata/telegraf/internal" "github.com/influxdata/telegraf/plugins/processors" ) @@ -12,11 +15,25 @@ const sampleConfig = ` ## Date format string, must be a representation of the Go "reference time" ## which is "Mon Jan 2 15:04:05 -0700 MST 2006". date_format = "Jan" + + ## Offset duration added to the date string when writing the new tag. + # date_offset = "0s" + + ## Timezone to use when creating the tag. 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" + type Date struct { - TagKey string `toml:"tag_key"` - DateFormat string `toml:"date_format"` + TagKey string `toml:"tag_key"` + DateFormat string `toml:"date_format"` + DateOffset internal.Duration `toml:"date_offset"` + Timezone string `toml:"timezone"` + + location *time.Location } func (d *Date) SampleConfig() string { @@ -27,9 +44,17 @@ func (d *Date) Description() string { return "Dates measurements, tags, and fields that pass through this filter." } +func (d *Date) Init() error { + var err error + // LoadLocation returns UTC if timezone is the empty string. + d.location, err = time.LoadLocation(d.Timezone) + return err +} + func (d *Date) Apply(in ...telegraf.Metric) []telegraf.Metric { for _, point := range in { - point.AddTag(d.TagKey, point.Time().Format(d.DateFormat)) + tm := point.Time().In(d.location).Add(d.DateOffset.Duration) + point.AddTag(d.TagKey, tm.Format(d.DateFormat)) } return in @@ -37,6 +62,8 @@ func (d *Date) Apply(in ...telegraf.Metric) []telegraf.Metric { func init() { processors.Add("date", func() telegraf.Processor { - return &Date{} + return &Date{ + Timezone: defaultTimezone, + } }) } diff --git a/plugins/processors/date/date_test.go b/plugins/processors/date/date_test.go index 98d88b351..d97cc2a9c 100644 --- a/plugins/processors/date/date_test.go +++ b/plugins/processors/date/date_test.go @@ -5,8 +5,11 @@ import ( "time" "github.com/influxdata/telegraf" + "github.com/influxdata/telegraf/internal" "github.com/influxdata/telegraf/metric" + "github.com/influxdata/telegraf/testutil" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func MustMetric(name string, tags map[string]string, fields map[string]interface{}, metricTime time.Time) telegraf.Metric { @@ -25,6 +28,8 @@ func TestMonthTag(t *testing.T) { TagKey: "month", DateFormat: "Jan", } + err := dateFormatMonth.Init() + require.NoError(t, err) currentTime := time.Now() month := currentTime.Format("Jan") @@ -43,6 +48,10 @@ func TestYearTag(t *testing.T) { TagKey: "year", DateFormat: "2006", } + + err := dateFormatYear.Init() + require.NoError(t, err) + currentTime := time.Now() year := currentTime.Format("2006") @@ -61,7 +70,46 @@ func TestOldDateTag(t *testing.T) { DateFormat: "2006", } + err := dateFormatYear.Init() + require.NoError(t, err) + m7 := MustMetric("foo", nil, nil, time.Date(1993, 05, 27, 0, 0, 0, 0, time.UTC)) customDateApply := dateFormatYear.Apply(m7) assert.Equal(t, map[string]string{"year": "1993"}, customDateApply[0].Tags(), "should add tag 'year'") } + +func TestDateOffset(t *testing.T) { + plugin := &Date{ + TagKey: "hour", + DateFormat: "15", + DateOffset: internal.Duration{Duration: 2 * time.Hour}, + } + + err := plugin.Init() + require.NoError(t, err) + + metric := testutil.MustMetric( + "cpu", + map[string]string{}, + map[string]interface{}{ + "time_idle": 42.0, + }, + time.Unix(1578603600, 0), + ) + + expected := []telegraf.Metric{ + testutil.MustMetric( + "cpu", + map[string]string{ + "hour": "23", + }, + map[string]interface{}{ + "time_idle": 42.0, + }, + time.Unix(1578603600, 0), + ), + } + + actual := plugin.Apply(metric) + testutil.RequireMetricsEqual(t, expected, actual) +}