1
0
Fork 0

Adding upstream version 1.34.4.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-05-24 07:26:29 +02:00
parent e393c3af3f
commit 4978089aab
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
4963 changed files with 677545 additions and 0 deletions

View file

@ -0,0 +1,68 @@
# Date Processor Plugin
Use the `date` processor to add the metric timestamp as a human readable tag.
A common use is to add a tag that can be used to group by month or year.
A few example usecases include:
1) consumption data for utilities on per month basis
2) bandwidth capacity per month
3) compare energy production or sales on a yearly or monthly basis
## 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
# Dates measurements, tags, and fields that pass through this filter.
[[processors.date]]
## New tag to create
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"
## which is "Mon Jan 2 15:04:05 -0700 MST 2006".
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.
# 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"
```
### 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]:
```text
set ZONEINFO=C:\zoneinfo.zip
```
## Example
```diff
- 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

View file

@ -0,0 +1,77 @@
//go:generate ../../../tools/readme_config_includer/generator
package date
import (
_ "embed"
"errors"
"time"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/config"
"github.com/influxdata/telegraf/plugins/processors"
)
//go:embed sample.conf
var sampleConfig string
const defaultTimezone = "UTC"
type Date struct {
TagKey string `toml:"tag_key"`
FieldKey string `toml:"field_key"`
DateFormat string `toml:"date_format"`
DateOffset config.Duration `toml:"date_offset"`
Timezone string `toml:"timezone"`
location *time.Location
}
func (*Date) SampleConfig() string {
return sampleConfig
}
func (d *Date) Init() error {
// Check either TagKey or FieldKey specified
if len(d.FieldKey) > 0 && len(d.TagKey) > 0 {
return errors.New("field_key and tag_key cannot be specified at the same time")
} else if len(d.FieldKey) == 0 && len(d.TagKey) == 0 {
return errors.New("at least one of field_key or tag_key must be specified")
}
// LoadLocation returns UTC if timezone is the empty string.
var err error
d.location, err = time.LoadLocation(d.Timezone)
return err
}
func (d *Date) Apply(in ...telegraf.Metric) []telegraf.Metric {
for _, point := range in {
tm := point.Time().In(d.location).Add(time.Duration(d.DateOffset))
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
}
func init() {
processors.Add("date", func() telegraf.Processor {
return &Date{
Timezone: defaultTimezone,
}
})
}

View file

@ -0,0 +1,294 @@
package date
import (
"sync"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/config"
"github.com/influxdata/telegraf/metric"
"github.com/influxdata/telegraf/testutil"
)
func TestTagAndField(t *testing.T) {
plugin := &Date{
TagKey: "month",
FieldKey: "month",
}
require.Error(t, plugin.Init())
}
func TestNoOutputSpecified(t *testing.T) {
plugin := &Date{}
require.Error(t, plugin.Init())
}
func TestMonthTag(t *testing.T) {
now := time.Now()
month := now.Format("Jan")
input := []telegraf.Metric{
metric.New("foo", map[string]string{}, map[string]interface{}{"value": 42}, now),
metric.New("bar", map[string]string{}, map[string]interface{}{"value": 42}, now),
metric.New("baz", map[string]string{}, map[string]interface{}{"value": 42}, now),
}
expected := []telegraf.Metric{
metric.New("foo", map[string]string{"month": month}, map[string]interface{}{"value": 42}, now),
metric.New("bar", map[string]string{"month": month}, map[string]interface{}{"value": 42}, now),
metric.New("baz", map[string]string{"month": month}, map[string]interface{}{"value": 42}, now),
}
plugin := &Date{
TagKey: "month",
DateFormat: "Jan",
}
require.NoError(t, plugin.Init())
actual := plugin.Apply(input...)
testutil.RequireMetricsEqual(t, expected, actual)
}
func TestMonthField(t *testing.T) {
now := time.Now()
month := now.Format("Jan")
input := []telegraf.Metric{
metric.New("foo", map[string]string{}, map[string]interface{}{"value": 42}, now),
metric.New("bar", map[string]string{}, map[string]interface{}{"value": 42}, now),
metric.New("baz", map[string]string{}, map[string]interface{}{"value": 42}, now),
}
expected := []telegraf.Metric{
metric.New("foo", map[string]string{}, map[string]interface{}{"value": 42, "month": month}, now),
metric.New("bar", map[string]string{}, map[string]interface{}{"value": 42, "month": month}, now),
metric.New("baz", map[string]string{}, map[string]interface{}{"value": 42, "month": month}, now),
}
plugin := &Date{
FieldKey: "month",
DateFormat: "Jan",
}
require.NoError(t, plugin.Init())
actual := plugin.Apply(input...)
testutil.RequireMetricsEqual(t, expected, actual)
}
func TestOldDateTag(t *testing.T) {
now := time.Date(1993, 05, 27, 0, 0, 0, 0, time.UTC)
input := []telegraf.Metric{
metric.New("foo", map[string]string{}, map[string]interface{}{"value": 42}, now),
metric.New("bar", map[string]string{}, map[string]interface{}{"value": 42}, now),
metric.New("baz", map[string]string{}, map[string]interface{}{"value": 42}, now),
}
expected := []telegraf.Metric{
metric.New("foo", map[string]string{"year": "1993"}, map[string]interface{}{"value": 42}, now),
metric.New("bar", map[string]string{"year": "1993"}, map[string]interface{}{"value": 42}, now),
metric.New("baz", map[string]string{"year": "1993"}, map[string]interface{}{"value": 42}, now),
}
plugin := &Date{
TagKey: "year",
DateFormat: "2006",
}
require.NoError(t, plugin.Init())
actual := plugin.Apply(input...)
testutil.RequireMetricsEqual(t, expected, actual)
}
func TestFieldUnix(t *testing.T) {
now := time.Now()
ts := now.Unix()
input := []telegraf.Metric{
metric.New("foo", map[string]string{}, map[string]interface{}{"value": 42}, now),
metric.New("bar", map[string]string{}, map[string]interface{}{"value": 42}, now),
metric.New("baz", map[string]string{}, map[string]interface{}{"value": 42}, now),
}
expected := []telegraf.Metric{
metric.New("foo", map[string]string{}, map[string]interface{}{"value": 42, "unix": ts}, now),
metric.New("bar", map[string]string{}, map[string]interface{}{"value": 42, "unix": ts}, now),
metric.New("baz", map[string]string{}, map[string]interface{}{"value": 42, "unix": ts}, now),
}
plugin := &Date{
FieldKey: "unix",
DateFormat: "unix",
}
require.NoError(t, plugin.Init())
actual := plugin.Apply(input...)
testutil.RequireMetricsEqual(t, expected, actual)
}
func TestFieldUnixNano(t *testing.T) {
now := time.Now()
ts := now.UnixNano()
input := []telegraf.Metric{
metric.New("foo", map[string]string{}, map[string]interface{}{"value": 42}, now),
metric.New("bar", map[string]string{}, map[string]interface{}{"value": 42}, now),
metric.New("baz", map[string]string{}, map[string]interface{}{"value": 42}, now),
}
expected := []telegraf.Metric{
metric.New("foo", map[string]string{}, map[string]interface{}{"value": 42, "unix_ns": ts}, now),
metric.New("bar", map[string]string{}, map[string]interface{}{"value": 42, "unix_ns": ts}, now),
metric.New("baz", map[string]string{}, map[string]interface{}{"value": 42, "unix_ns": ts}, now),
}
plugin := &Date{
FieldKey: "unix_ns",
DateFormat: "unix_ns",
}
require.NoError(t, plugin.Init())
actual := plugin.Apply(input...)
testutil.RequireMetricsEqual(t, expected, actual)
}
func TestFieldUnixMillis(t *testing.T) {
now := time.Now()
ts := now.UnixMilli()
input := []telegraf.Metric{
metric.New("foo", map[string]string{}, map[string]interface{}{"value": 42}, now),
metric.New("bar", map[string]string{}, map[string]interface{}{"value": 42}, now),
metric.New("baz", map[string]string{}, map[string]interface{}{"value": 42}, now),
}
expected := []telegraf.Metric{
metric.New("foo", map[string]string{}, map[string]interface{}{"value": 42, "unix_ms": ts}, now),
metric.New("bar", map[string]string{}, map[string]interface{}{"value": 42, "unix_ms": ts}, now),
metric.New("baz", map[string]string{}, map[string]interface{}{"value": 42, "unix_ms": ts}, now),
}
plugin := &Date{
FieldKey: "unix_ms",
DateFormat: "unix_ms",
}
require.NoError(t, plugin.Init())
actual := plugin.Apply(input...)
testutil.RequireMetricsEqual(t, expected, actual)
}
func TestFieldUnixMicros(t *testing.T) {
now := time.Now()
ts := now.UnixMicro()
input := []telegraf.Metric{
metric.New("foo", map[string]string{}, map[string]interface{}{"value": 42}, now),
metric.New("bar", map[string]string{}, map[string]interface{}{"value": 42}, now),
metric.New("baz", map[string]string{}, map[string]interface{}{"value": 42}, now),
}
expected := []telegraf.Metric{
metric.New("foo", map[string]string{}, map[string]interface{}{"value": 42, "unix_us": ts}, now),
metric.New("bar", map[string]string{}, map[string]interface{}{"value": 42, "unix_us": ts}, now),
metric.New("baz", map[string]string{}, map[string]interface{}{"value": 42, "unix_us": ts}, now),
}
plugin := &Date{
FieldKey: "unix_us",
DateFormat: "unix_us",
}
require.NoError(t, plugin.Init())
actual := plugin.Apply(input...)
testutil.RequireMetricsEqual(t, expected, actual)
}
func TestDateOffset(t *testing.T) {
plugin := &Date{
TagKey: "hour",
DateFormat: "15",
DateOffset: config.Duration(2 * time.Hour),
}
require.NoError(t, plugin.Init())
input := 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(input)
testutil.RequireMetricsEqual(t, expected, actual)
}
func TestTracking(t *testing.T) {
now := time.Now()
ts := now.UnixMicro()
inputRaw := []telegraf.Metric{
metric.New("foo", map[string]string{}, map[string]interface{}{"value": 42}, now),
metric.New("bar", map[string]string{}, map[string]interface{}{"value": 42}, now),
metric.New("baz", map[string]string{}, map[string]interface{}{"value": 42}, now),
}
var mu sync.Mutex
delivered := make([]telegraf.DeliveryInfo, 0, len(inputRaw))
notify := func(di telegraf.DeliveryInfo) {
mu.Lock()
defer mu.Unlock()
delivered = append(delivered, di)
}
input := make([]telegraf.Metric, 0, len(inputRaw))
expected := make([]telegraf.Metric, 0, len(input))
for _, m := range inputRaw {
tm, _ := metric.WithTracking(m, notify)
input = append(input, tm)
em := m.Copy()
em.AddField("unix_us", ts)
expected = append(expected, m)
}
plugin := &Date{
FieldKey: "unix_us",
DateFormat: "unix_us",
}
require.NoError(t, plugin.Init())
// Process expected metrics and compare with resulting metrics
actual := plugin.Apply(input...)
testutil.RequireMetricsEqual(t, expected, actual)
// Simulate output acknowledging delivery
for _, m := range actual {
m.Accept()
}
// Check delivery
require.Eventuallyf(t, func() bool {
mu.Lock()
defer mu.Unlock()
return len(input) == len(delivered)
}, time.Second, 100*time.Millisecond, "%d delivered but %d expected", len(delivered), len(expected))
}

View file

@ -0,0 +1,24 @@
# Dates measurements, tags, and fields that pass through this filter.
[[processors.date]]
## New tag to create
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"
## which is "Mon Jan 2 15:04:05 -0700 MST 2006".
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.
# 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"