feat(inputs.statsd): Allow counters to report as float (#15371)

This commit is contained in:
Joshua Powers 2024-05-17 11:57:50 -06:00 committed by GitHub
parent 5239c97ce0
commit 1e6eed54c2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 125 additions and 0 deletions

View File

@ -117,6 +117,11 @@ See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
## Replace dots (.) with underscore (_) and dashes (-) with
## double underscore (__) in metric names.
# convert_names = false
## Convert all numeric counters to float
## Enabling this would ensure that both counters and guages are both emitted
## as floats.
# float_counters = false
```
## Description

View File

@ -90,3 +90,8 @@
## Replace dots (.) with underscore (_) and dashes (-) with
## double underscore (__) in metric names.
# convert_names = false
## Convert all numeric counters to float
## Enabling this would ensure that both counters and guages are both emitted
## as floats.
# float_counters = false

View File

@ -76,6 +76,7 @@ type Statsd struct {
DeleteSets bool `toml:"delete_sets"`
DeleteTimings bool `toml:"delete_timings"`
ConvertNames bool `toml:"convert_names"`
FloatCounters bool `toml:"float_counters"`
EnableAggregationTemporality bool `toml:"enable_aggregation_temporality"`
@ -285,6 +286,11 @@ func (s *Statsd) Gather(acc telegraf.Accumulator) error {
m.fields["start_time"] = s.lastGatherTime.Format(time.RFC3339)
}
if s.FloatCounters {
for key := range m.fields {
m.fields[key] = float64(m.fields[key].(int64))
}
}
acc.AddCounter(m.name, m.fields, m.tags, now)
}
if s.DeleteCounters {

View File

@ -532,6 +532,115 @@ func TestParse_Counters(t *testing.T) {
}
}
func TestParse_CountersAsFloat(t *testing.T) {
s := NewTestStatsd()
s.FloatCounters = true
// Test that counters work
validLines := []string{
"small.inc:1|c",
"big.inc:100|c",
"big.inc:1|c",
"big.inc:100000|c",
"big.inc:1000000|c",
"small.inc:1|c",
"zero.init:0|c",
"sample.rate:1|c|@0.1",
"sample.rate:1|c",
"scientific.notation:4.696E+5|c",
"negative.test:100|c",
"negative.test:-5|c",
}
for _, line := range validLines {
require.NoErrorf(t, s.parseStatsdLine(line), "Parsing line %s should not have resulted in an error", line)
}
validations := []struct {
name string
value int64
}{
{
"scientific_notation",
469600,
},
{
"small_inc",
2,
},
{
"big_inc",
1100101,
},
{
"zero_init",
0,
},
{
"sample_rate",
11,
},
{
"negative_test",
95,
},
}
for _, test := range validations {
require.NoError(t, testValidateCounter(test.name, test.value, s.counters))
}
expected := []telegraf.Metric{
testutil.MustMetric(
"small_inc",
map[string]string{"metric_type": "counter"},
map[string]interface{}{"value": 2.0},
time.Now(),
telegraf.Counter,
),
testutil.MustMetric(
"big_inc",
map[string]string{"metric_type": "counter"},
map[string]interface{}{"value": 1100101.0},
time.Now(),
telegraf.Counter,
),
testutil.MustMetric(
"zero_init",
map[string]string{"metric_type": "counter"},
map[string]interface{}{"value": 0.0},
time.Now(),
telegraf.Counter,
),
testutil.MustMetric(
"sample_rate",
map[string]string{"metric_type": "counter"},
map[string]interface{}{"value": 11.0},
time.Now(),
telegraf.Counter,
),
testutil.MustMetric(
"scientific_notation",
map[string]string{"metric_type": "counter"},
map[string]interface{}{"value": 469600.0},
time.Now(),
telegraf.Counter,
),
testutil.MustMetric(
"negative_test",
map[string]string{"metric_type": "counter"},
map[string]interface{}{"value": 95.0},
time.Now(),
telegraf.Counter,
),
}
acc := &testutil.Accumulator{}
require.NoError(t, s.Gather(acc))
metrics := acc.GetTelegrafMetrics()
testutil.PrintMetrics(metrics)
testutil.RequireMetricsEqual(t, expected, metrics, testutil.IgnoreTime(), testutil.SortMetrics())
}
// Tests low-level functionality of timings
func TestParse_Timings(t *testing.T) {
s := NewTestStatsd()