Skip to content

Commit 414cfa1

Browse files
authored
Support latest DogStatsD version (#31296)
**Description:** * Support DogStatsD v1.2 by accepting metrics with container IDs * Support DogStatsD v1.3 by accepting metrics with timestamps * Add tests for the cases above * Improve readability in `statsd_parser_test.go` when calling `testStatsDMetric` * Add changelog entry **Link to tracking Issue:** #31295 **Testing:** 1. Added unit tests to validate that the new fields are accepted and interpreted correctly 2. Manual tests to verify that the metrics go through a collector pipeline. Notice the `container_id` attribute and the `Timestamp` field, which contains a custom value different from `StartTimestamp`: ``` ScopeMetrics #0 ScopeMetrics SchemaURL: InstrumentationScope otelcol/statsdreceiver 0.94.0-dev Metric #0 Descriptor: -> Name: workflow_execution.failure -> Description: -> Unit: -> DataType: Sum -> IsMonotonic: false -> AggregationTemporality: Delta NumberDataPoints #0 Data point attributes: -> container_id: Str(d69b8773f0ac1d93447a5835cb4ed1bf13dd336d32994830687ae1f352c86fe4) -> deployment.id: Str(d_0lBskd) -> metric_type: Str(counter) -> env: Str(development) -> project.id: Str(proj_kYRs18) -> trace.id: Str(2cQtSfsef2TN3EC2psGIorPaFd8) -> workflow.id: Str(p_0PACqQ) StartTimestamp: 2024-02-16 03:53:30.323425523 +0000 UTC Timestamp: 2024-02-16 03:53:51 +0000 UTC Value: 1 ```
1 parent 7fa5060 commit 414cfa1

File tree

5 files changed

+575
-70
lines changed

5 files changed

+575
-70
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Use this changelog template to create an entry for release notes.
2+
3+
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
4+
change_type: enhancement
5+
6+
# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
7+
component: statsdreceiver
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: Add support for the latest version of DogStatsD protocol (v1.3)
11+
12+
# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
13+
issues: [31295]
14+
15+
# (Optional) One or more lines of additional information to render under the primary note.
16+
# These lines will be padded with 2 spaces and then inserted directly into the document.
17+
# Use pipe (|) for multiline entries.
18+
subtext:
19+
20+
# If your change doesn't affect end users or the exported elements of any package,
21+
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
22+
# Optional: The change log or logs in which this entry should be included.
23+
# e.g. '[user]' or '[user, api]'
24+
# Include 'user' if the change is relevant to end users.
25+
# Include 'api' if there is a change to a library API.
26+
# Default: '[user]'
27+
change_logs: [user]

receiver/statsdreceiver/go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ require (
1414
go.opentelemetry.io/collector/consumer v0.95.0
1515
go.opentelemetry.io/collector/pdata v1.2.0
1616
go.opentelemetry.io/collector/receiver v0.95.0
17+
go.opentelemetry.io/collector/semconv v0.95.0
1718
go.opentelemetry.io/otel v1.23.1
1819
go.opentelemetry.io/otel/metric v1.23.1
1920
go.opentelemetry.io/otel/trace v1.23.1

receiver/statsdreceiver/internal/protocol/metric_translator.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,20 @@ func buildCounterMetric(parsedMetric statsDMetric, isMonotonicCounter bool) pmet
3434
dp.Attributes().PutStr(string(i.Attribute().Key), i.Attribute().Value.AsString())
3535
}
3636

37+
if parsedMetric.timestamp != 0 {
38+
dp.SetTimestamp(pcommon.Timestamp(parsedMetric.timestamp))
39+
}
40+
3741
return ilm
3842
}
3943

4044
func setTimestampsForCounterMetric(ilm pmetric.ScopeMetrics, startTime, timeNow time.Time) {
4145
dp := ilm.Metrics().At(0).Sum().DataPoints().At(0)
4246
dp.SetStartTimestamp(pcommon.NewTimestampFromTime(startTime))
43-
dp.SetTimestamp(pcommon.NewTimestampFromTime(timeNow))
47+
48+
if dp.Timestamp() == 0 {
49+
dp.SetTimestamp(pcommon.NewTimestampFromTime(timeNow))
50+
}
4451
}
4552

4653
func buildGaugeMetric(parsedMetric statsDMetric, timeNow time.Time) pmetric.ScopeMetrics {

receiver/statsdreceiver/internal/protocol/statsd_parser.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"go.opentelemetry.io/collector/component"
1717
"go.opentelemetry.io/collector/pdata/pcommon"
1818
"go.opentelemetry.io/collector/pdata/pmetric"
19+
semconv "go.opentelemetry.io/collector/semconv/v1.22.0"
1920
"go.opentelemetry.io/otel/attribute"
2021
)
2122

@@ -128,6 +129,7 @@ type statsDMetric struct {
128129
addition bool
129130
unit string
130131
sampleRate float64
132+
timestamp uint64
131133
}
132134

133135
type statsDMetricDescription struct {
@@ -430,6 +432,28 @@ func parseMessageToMetric(line string, enableMetricType bool, enableSimpleTags b
430432

431433
kvs = append(kvs, attribute.String(k, v))
432434
}
435+
case strings.HasPrefix(part, "c:"):
436+
// As per DogStatD protocol v1.2:
437+
// https://docs.datadoghq.com/developers/dogstatsd/datagram_shell/?tab=metrics#dogstatsd-protocol-v12
438+
containerID := strings.TrimPrefix(part, "c:")
439+
440+
if containerID != "" {
441+
kvs = append(kvs, attribute.String(semconv.AttributeContainerID, containerID))
442+
}
443+
case strings.HasPrefix(part, "T"):
444+
// As per DogStatD protocol v1.3:
445+
// https://docs.datadoghq.com/developers/dogstatsd/datagram_shell/?tab=metrics#dogstatsd-protocol-v13
446+
if inType != CounterType && inType != GaugeType {
447+
return result, fmt.Errorf("only GAUGE and COUNT metrics support a timestamp")
448+
}
449+
450+
timestampStr := strings.TrimPrefix(part, "T")
451+
timestampSeconds, err := strconv.ParseUint(timestampStr, 10, 64)
452+
if err != nil {
453+
return result, fmt.Errorf("invalid timestamp: %s", timestampStr)
454+
}
455+
456+
result.timestamp = timestampSeconds * 1e9 // Convert seconds to nanoseconds
433457
default:
434458
return result, fmt.Errorf("unrecognized message part: %s", part)
435459
}

0 commit comments

Comments
 (0)