Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(processors.timestamp): Introduce timestamp processor #15094

Merged
merged 2 commits into from
Apr 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading