Skip to content

Commit

Permalink
feat(processors.timestamp): Introduce timestamp processor (#15094)
Browse files Browse the repository at this point in the history
  • Loading branch information
powersj authored Apr 11, 2024
1 parent 1416b8b commit 7acbf58
Show file tree
Hide file tree
Showing 5 changed files with 410 additions and 0 deletions.
5 changes: 5 additions & 0 deletions plugins/processors/all/processors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
//go:build !custom || processors || processors.timestamp

package all

import _ "github.com/influxdata/telegraf/plugins/processors/timestamp" // register plugin
98 changes: 98 additions & 0 deletions plugins/processors/timestamp/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# Timestamp Processor Plugin

Use the timestamp processor to parse fields containing timestamps into
timestamps of other formats.

## Global configuration options <!-- @/docs/includes/plugin_config.md -->

In addition to the plugin-specific configuration settings, plugins support
additional global and plugin configuration settings. These settings are used to
modify metrics, tags, and field or create aliases and configure ordering, etc.
See the [CONFIGURATION.md][CONFIGURATION.md] for more details.

[CONFIGURATION.md]: ../../../docs/CONFIGURATION.md#plugins

## Configuration

```toml @sample.conf
# Convert a timestamp field to other timestamp format
[[processors.timestamp]]
## Timestamp key to convert
## Specify the field name that contains the timestamp to convert. The result
## will replace the current field value.
field = ""

## Timestamp Format
## This defines the time layout used to interpret the source timestamp field.
## The time must be `unix`, `unix_ms`, `unix_us`, `unix_ns`, or a time in Go
## "reference time". For more information on Go "reference time". For more
## see: https://golang.org/pkg/time/#Time.Format
source_timestamp_format = ""

## Timestamp Timezone
## Source timestamp timezone. If not set, assumed to be in UTC.
## Options are as follows:
## 1. UTC -- or unspecified will return timestamp in UTC
## 2. Local -- interpret based on machine localtime
## 3. "America/New_York" -- Unix TZ values like those found in
## https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
# source_timestamp_timezone = ""

## Target timestamp format
## This defines the destination timestmap format. It also can accept either
## `unix`, `unix_ms`, `unix_us`, `unix_ns`, or a time in Go "reference time".
destination_timestamp_format = ""

## Target Timestamp Timezone
## Source timestamp timezone. If not set, assumed to be in UTC.
## Options are as follows:
## 1. UTC -- or unspecified will return timestamp in UTC
## 2. Local -- interpret based on machine localtime
## 3. "America/New_York" -- Unix TZ values like those found in
## https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
# destination_timestamp_timezone = ""
```

## Example

Convert a timestamp to unix timestamp:

```toml
[[processors.timestamp]]
source_timestamp_field = "timestamp"
source_timestamp_format = "2006-01-02T15:04:05.999999999Z"
destination_timestamp_format = "unix"
```

```diff
- metric value=42i,timestamp="2024-03-04T10:10:32.123456Z" 1560540094000000000
+ metric value=42i,timestamp=1709547032 1560540094000000000
```

Convert the same timestamp to a nanosecond unix timestamp:

```toml
[[processors.timestamp]]
source_timestamp_field = "timestamp"
source_timestamp_format = "2006-01-02T15:04:05.999999999Z"
destination_timestamp_format = "unix_ns"
```

```diff
- metric value=42i,timestamp="2024-03-04T10:10:32.123456789Z" 1560540094000000000
+ metric value=42i,timestamp=1709547032123456789 1560540094000000000
```

Convert the timestamp to another timestamp format:

```toml
[[processors.timestamp]]
source_timestamp_field = "timestamp"
source_timestamp_format = "2006-01-02T15:04:05.999999999Z"
destination_timestamp_format = "2006-01-02T15:04"
```

```diff
- metric value=42i,timestamp="2024-03-04T10:10:32.123456Z" 1560540094000000000
+ metric value=42i,timestamp="2024-03-04T10:10" 1560540094000000000
```
36 changes: 36 additions & 0 deletions plugins/processors/timestamp/sample.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Convert a timestamp field to other timestamp format
[[processors.timestamp]]
## Timestamp key to convert
## Specify the field name that contains the timestamp to convert. The result
## will replace the current field value.
field = ""

## Timestamp Format
## This defines the time layout used to interpret the source timestamp field.
## The time must be `unix`, `unix_ms`, `unix_us`, `unix_ns`, or a time in Go
## "reference time". For more information on Go "reference time". For more
## see: https://golang.org/pkg/time/#Time.Format
source_timestamp_format = ""

## Timestamp Timezone
## Source timestamp timezone. If not set, assumed to be in UTC.
## Options are as follows:
## 1. UTC -- or unspecified will return timestamp in UTC
## 2. Local -- interpret based on machine localtime
## 3. "America/New_York" -- Unix TZ values like those found in
## https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
# source_timestamp_timezone = ""

## Target timestamp format
## This defines the destination timestmap format. It also can accept either
## `unix`, `unix_ms`, `unix_us`, `unix_ns`, or a time in Go "reference time".
destination_timestamp_format = ""

## Target Timestamp Timezone
## Source timestamp timezone. If not set, assumed to be in UTC.
## Options are as follows:
## 1. UTC -- or unspecified will return timestamp in UTC
## 2. Local -- interpret based on machine localtime
## 3. "America/New_York" -- Unix TZ values like those found in
## https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
# destination_timestamp_timezone = ""
107 changes: 107 additions & 0 deletions plugins/processors/timestamp/timestamp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
//go:generate ../../../tools/readme_config_includer/generator
package timestamp

import (
_ "embed"
"errors"
"fmt"
"time"

"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/internal"
"github.com/influxdata/telegraf/plugins/processors"
)

//go:embed sample.conf
var sampleConfig string

type Timestamp struct {
Field string `toml:"field"`
SourceFormat string `toml:"source_timestamp_format"`
SourceTimezone string `toml:"source_timestamp_timezone"`
DestinationFormat string `toml:"destination_timestamp_format"`
DestinationTimezone string `toml:"destination_timestamp_timezone"`

sourceLocation *time.Location
destinationLocation *time.Location
}

func (*Timestamp) SampleConfig() string {
return sampleConfig
}

func (t *Timestamp) Init() error {
switch t.SourceFormat {
case "":
return errors.New("source_timestamp_format is required")
case "unix", "unix_ms", "unix_us", "unix_ns":
default:
if time.Now().Format(t.SourceFormat) == t.SourceFormat {
return fmt.Errorf("invalid timestamp format %q", t.SourceFormat)
}
}

switch t.DestinationFormat {
case "":
return errors.New("source_timestamp_format is required")
case "unix", "unix_ms", "unix_us", "unix_ns":
default:
if time.Now().Format(t.DestinationFormat) == t.DestinationFormat {
return fmt.Errorf("invalid timestamp format %q", t.DestinationFormat)
}
}

if t.SourceTimezone == "" {
t.SourceTimezone = "UTC"
}

// LoadLocation returns UTC if timezone is the empty string.
var err error
t.sourceLocation, err = time.LoadLocation(t.SourceTimezone)
if err != nil {
return fmt.Errorf("invalid source_timestamp_timezone %q: %w", t.SourceTimezone, err)
}

if t.DestinationTimezone == "" {
t.DestinationTimezone = "UTC"
}
t.destinationLocation, err = time.LoadLocation(t.DestinationTimezone)
if err != nil {
return fmt.Errorf("invalid source_timestamp_timezone %q: %w", t.DestinationTimezone, err)
}

return nil
}

func (t *Timestamp) Apply(in ...telegraf.Metric) []telegraf.Metric {
for _, point := range in {
if field, ok := point.GetField(t.Field); ok {
timestamp, err := internal.ParseTimestamp(t.SourceFormat, field, t.sourceLocation)
if err != nil {
continue
}

switch t.DestinationFormat {
case "unix":
point.AddField(t.Field, timestamp.Unix())
case "unix_ms":
point.AddField(t.Field, timestamp.UnixNano()/1000000)
case "unix_us":
point.AddField(t.Field, timestamp.UnixNano()/1000)
case "unix_ns":
point.AddField(t.Field, timestamp.UnixNano())
default:
inLocation := timestamp.In(t.destinationLocation)
point.AddField(t.Field, inLocation.Format(t.DestinationFormat))
}
}
}

return in
}

func init() {
processors.Add("timestamp", func() telegraf.Processor {
return &Timestamp{}
})
}
Loading

0 comments on commit 7acbf58

Please sign in to comment.