Skip to content

Commit 9ca2a2e

Browse files
authoredSep 10, 2020
Merge pull request #343 from atlassian/jdu/coerce-non-numeric-values
Coerce non-numeric values to numeric in the datadog backend
2 parents 3ea558e + fe43bd2 commit 9ca2a2e

File tree

4 files changed

+52
-5
lines changed

4 files changed

+52
-5
lines changed
 

‎CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
28.3.0
2+
------
3+
- Rather than dropping data points, the Datadog backend will coerce non-numeric values resulting from aggregation to numeric.
4+
`NaN` is converted to -1, `+Inf` to float64 maximum, and `-Inf` to float64 minimum.
5+
16
28.2.1
27
------
38
- Document how semver is used in the codebase.

‎go.sum

+1
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ=
198198
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
199199
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
200200
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
201+
github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=
201202
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
202203
github.com/jingyugao/rowserrcheck v0.0.0-20191204022205-72ab7603b68a h1:GmsqmapfzSJkm28dhRoHz2tLRbJmqhU86IPgBtN3mmk=
203204
github.com/jingyugao/rowserrcheck v0.0.0-20191204022205-72ab7603b68a/go.mod h1:xRskid8CManxVta/ALEhJha/pweKBaVG6fWgc0yH25s=

‎pkg/backends/datadog/flush.go

+15-5
Original file line numberDiff line numberDiff line change
@@ -51,21 +51,31 @@ func (f *flush) addMetricf(metricType metricType, value float64, source gostatsd
5151
}
5252

5353
// addMetric adds a metric to the series.
54+
// If the value is non-numeric (in the case of NaN and Inf values), the value is coerced into a numeric value.
5455
func (f *flush) addMetric(metricType metricType, value float64, source gostatsd.Source, tags gostatsd.Tags, name string) {
55-
if math.IsInf(value, 1) || math.IsInf(value, -1) || math.IsNaN(value) {
56-
// The value can not be represented within the JSON payload so it is to be discarded.
57-
return
58-
}
5956
f.ts.Series = append(f.ts.Series, metric{
6057
Host: string(source),
6158
Interval: f.flushIntervalSec,
6259
Metric: name,
63-
Points: [1]point{{f.timestamp, value}},
60+
Points: [1]point{{f.timestamp, coerceToNumeric(value)}},
6461
Tags: tags,
6562
Type: metricType,
6663
})
6764
}
6865

66+
// coerceToNumeric will convert non-numeric NaN and Inf values to a numeric value.
67+
// If v is a numeric, the same value is returned.
68+
func coerceToNumeric(v float64) float64 {
69+
if math.IsNaN(v) {
70+
return -1
71+
} else if math.IsInf(v, 1) {
72+
return math.MaxFloat64
73+
} else if math.IsInf(v, -1) {
74+
return -math.MaxFloat64
75+
}
76+
return v
77+
}
78+
6979
func (f *flush) maybeFlush() {
7080
if uint(len(f.ts.Series))+20 >= f.metricsPerBatch { // flush before it reaches max size and grows the slice
7181
f.cb(f.ts)

‎pkg/backends/datadog/flush_test.go

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package datadog
2+
3+
import (
4+
"math"
5+
"testing"
6+
)
7+
8+
func TestCoerceToNumeric(t *testing.T) {
9+
t.Parallel()
10+
tests := []struct {
11+
name string
12+
arg float64
13+
want float64
14+
}{
15+
{"NaN should return 0", math.NaN(), -1},
16+
{"+Inf should return maximum float value", math.Inf(+1), math.MaxFloat64},
17+
{"-Inf should return minimum float value", math.Inf(-1), -math.MaxFloat64},
18+
{"Numeric value should return unchanged", 0, 0},
19+
{"Numeric value should return unchanged", 12_345, 12_345},
20+
{"Numeric value should return unchanged", -12_345, -12_345},
21+
{"Numeric value should return unchanged", math.MaxFloat64, math.MaxFloat64},
22+
{"-Inf should return minimum float value", -math.MaxFloat64, -math.MaxFloat64},
23+
}
24+
for _, tt := range tests {
25+
t.Run(tt.name, func(t *testing.T) {
26+
if got := coerceToNumeric(tt.arg); got != tt.want {
27+
t.Errorf("coerceToNumeric() = %v, want %v", got, tt.want)
28+
}
29+
})
30+
}
31+
}

0 commit comments

Comments
 (0)
Failed to load comments.