diff --git a/plugins/inputs/statsd/README.md b/plugins/inputs/statsd/README.md
index 891c59198..a376484c2 100644
--- a/plugins/inputs/statsd/README.md
+++ b/plugins/inputs/statsd/README.md
@@ -81,6 +81,11 @@ See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
## https://docs.datadoghq.com/developers/metrics/types/?tab=distribution#definition
datadog_distributions = false
+ ## Keep or drop the container id as tag. Included as optional field
+ ## in DogStatsD protocol v1.2 if source is running in Kubernetes
+ ## https://docs.datadoghq.com/developers/dogstatsd/datagram_shell/?tab=metrics#dogstatsd-protocol-v12
+ datadog_keep_container_tag = false
+
## Statsd data translation templates, more info can be read here:
## https://github.com/influxdata/telegraf/blob/master/docs/TEMPLATE_PATTERN.md
# templates = [
@@ -259,6 +264,7 @@ measurements and tags.
- **parse_data_dog_tags** boolean: Enable parsing of tags in DataDog's dogstatsd format ()
- **datadog_extensions** boolean: Enable parsing of DataDog's extensions to dogstatsd format ()
- **datadog_distributions** boolean: Enable parsing of the Distribution metric in DataDog's dogstatsd format ()
+- **datadog_keep_container_tag** boolean: Keep or drop the container id as tag. Included as optional field in DogStatsD protocol v1.2 if source is running in Kubernetes.
- **max_ttl** config.Duration: Max duration (TTL) for each metric to stay cached/reported without being updated.
## Statsd bucket -> InfluxDB line-protocol Templates
diff --git a/plugins/inputs/statsd/sample.conf b/plugins/inputs/statsd/sample.conf
index 7c63699b9..4f7fbb50a 100644
--- a/plugins/inputs/statsd/sample.conf
+++ b/plugins/inputs/statsd/sample.conf
@@ -54,6 +54,11 @@
## https://docs.datadoghq.com/developers/metrics/types/?tab=distribution#definition
datadog_distributions = false
+ ## Keep or drop the container id as tag. Included as optional field
+ ## in DogStatsD protocol v1.2 if source is running in Kubernetes
+ ## https://docs.datadoghq.com/developers/dogstatsd/datagram_shell/?tab=metrics#dogstatsd-protocol-v12
+ datadog_keep_container_tag = false
+
## Statsd data translation templates, more info can be read here:
## https://github.com/influxdata/telegraf/blob/master/docs/TEMPLATE_PATTERN.md
# templates = [
diff --git a/plugins/inputs/statsd/statsd.go b/plugins/inputs/statsd/statsd.go
index 6579f0d32..b90794db2 100644
--- a/plugins/inputs/statsd/statsd.go
+++ b/plugins/inputs/statsd/statsd.go
@@ -96,6 +96,11 @@ type Statsd struct {
// https://docs.datadoghq.com/developers/metrics/types/?tab=distribution#definition
DataDogDistributions bool `toml:"datadog_distributions"`
+ // Either to keep or drop the container id as tag.
+ // Requires the DataDogExtension flag to be enabled.
+ // https://docs.datadoghq.com/developers/dogstatsd/datagram_shell/?tab=metrics#dogstatsd-protocol-v12
+ DataDogKeepContainerTag bool `toml:"datadog_keep_container_tag"`
+
// UDPPacketSize is deprecated, it's only here for legacy support
// we now always create 1 max size buffer and then copy only what we need
// into the in channel
@@ -577,6 +582,11 @@ func (s *Statsd) parseStatsdLine(line string) error {
if len(segment) > 0 && segment[0] == '#' {
// we have ourselves a tag; they are comma separated
parseDataDogTags(lineTags, segment[1:])
+ } else if len(segment) > 0 && strings.HasPrefix(segment, "c:") {
+ // This is optional container ID field
+ if s.DataDogKeepContainerTag {
+ lineTags["container"] = segment[2:]
+ }
} else {
recombinedSegments = append(recombinedSegments, segment)
}
diff --git a/plugins/inputs/statsd/statsd_test.go b/plugins/inputs/statsd/statsd_test.go
index c8af8fa71..2f7a0fa83 100644
--- a/plugins/inputs/statsd/statsd_test.go
+++ b/plugins/inputs/statsd/statsd_test.go
@@ -1206,6 +1206,157 @@ func TestParse_DataDogTags(t *testing.T) {
}
}
+func TestParse_DataDogContainerID(t *testing.T) {
+ tests := []struct {
+ name string
+ line string
+ keep bool
+ expected []telegraf.Metric
+ }{
+ {
+ name: "counter",
+ line: "my_counter:1|c|#host:localhost,endpoint:/:tenant?/oauth/ro|c:f76b5a1c03caa192580874b253c158010ade668cf03080a57aa8283919d56e75",
+ keep: true,
+ expected: []telegraf.Metric{
+ testutil.MustMetric(
+ "my_counter",
+ map[string]string{
+ "endpoint": "/:tenant?/oauth/ro",
+ "host": "localhost",
+ "metric_type": "counter",
+ "container": "f76b5a1c03caa192580874b253c158010ade668cf03080a57aa8283919d56e75",
+ },
+ map[string]interface{}{
+ "value": 1,
+ },
+ time.Now(),
+ telegraf.Counter,
+ ),
+ },
+ },
+ {
+ name: "gauge",
+ line: "my_gauge:10.1|g|#live|c:f76b5a1c03caa192580874b253c158010ade668cf03080a57aa8283919d56e75",
+ keep: true,
+ expected: []telegraf.Metric{
+ testutil.MustMetric(
+ "my_gauge",
+ map[string]string{
+ "live": "true",
+ "metric_type": "gauge",
+ "container": "f76b5a1c03caa192580874b253c158010ade668cf03080a57aa8283919d56e75",
+ },
+ map[string]interface{}{
+ "value": 10.1,
+ },
+ time.Now(),
+ telegraf.Gauge,
+ ),
+ },
+ },
+ {
+ name: "set",
+ line: "my_set:1|s|#host:localhost|c:f76b5a1c03caa192580874b253c158010ade668cf03080a57aa8283919d56e75",
+ keep: true,
+ expected: []telegraf.Metric{
+ testutil.MustMetric(
+ "my_set",
+ map[string]string{
+ "host": "localhost",
+ "metric_type": "set",
+ "container": "f76b5a1c03caa192580874b253c158010ade668cf03080a57aa8283919d56e75",
+ },
+ map[string]interface{}{
+ "value": 1,
+ },
+ time.Now(),
+ ),
+ },
+ },
+ {
+ name: "timer",
+ line: "my_timer:3|ms|@0.1|#live,host:localhost|c:f76b5a1c03caa192580874b253c158010ade668cf03080a57aa8283919d56e75",
+ keep: true,
+ expected: []telegraf.Metric{
+ testutil.MustMetric(
+ "my_timer",
+ map[string]string{
+ "host": "localhost",
+ "live": "true",
+ "metric_type": "timing",
+ "container": "f76b5a1c03caa192580874b253c158010ade668cf03080a57aa8283919d56e75",
+ },
+ map[string]interface{}{
+ "count": 10,
+ "lower": float64(3),
+ "mean": float64(3),
+ "median": float64(3),
+ "stddev": float64(0),
+ "sum": float64(30),
+ "upper": float64(3),
+ },
+ time.Now(),
+ ),
+ },
+ },
+ {
+ name: "empty tag set",
+ line: "cpu:42|c|#|c:f76b5a1c03caa192580874b253c158010ade668cf03080a57aa8283919d56e75",
+ keep: true,
+ expected: []telegraf.Metric{
+ testutil.MustMetric(
+ "cpu",
+ map[string]string{
+ "metric_type": "counter",
+ "container": "f76b5a1c03caa192580874b253c158010ade668cf03080a57aa8283919d56e75",
+ },
+ map[string]interface{}{
+ "value": 42,
+ },
+ time.Now(),
+ telegraf.Counter,
+ ),
+ },
+ },
+ {
+ name: "drop it",
+ line: "cpu:42|c|#live,host:localhost|c:f76b5a1c03caa192580874b253c158010ade668cf03080a57aa8283919d56e75",
+ keep: false,
+ expected: []telegraf.Metric{
+ testutil.MustMetric(
+ "cpu",
+ map[string]string{
+ "host": "localhost",
+ "live": "true",
+ "metric_type": "counter",
+ },
+ map[string]interface{}{
+ "value": 42,
+ },
+ time.Now(),
+ telegraf.Counter,
+ ),
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ var acc testutil.Accumulator
+
+ s := NewTestStatsd()
+ s.DataDogExtensions = true
+ s.DataDogKeepContainerTag = tt.keep
+
+ require.NoError(t, s.parseStatsdLine(tt.line))
+ require.NoError(t, s.Gather(&acc))
+
+ testutil.RequireMetricsEqual(t, tt.expected, acc.GetTelegrafMetrics(),
+ testutil.SortMetrics(), testutil.IgnoreTime())
+ })
+ }
+}
+
// Test that statsd buckets are parsed to measurement names properly
func TestParseName(t *testing.T) {
s := NewTestStatsd()