From 61a6b5a1b853edf4686d457ff9b1b533fe8a45cf Mon Sep 17 00:00:00 2001 From: Pablo Baeyens Date: Fri, 13 Aug 2021 15:32:31 +0200 Subject: [PATCH] [exporter/datadogexporter] Export delta sums as counts (#4609) Exports OTLP delta sums (both monotonic and non-monotonic) as Datadog counts with no interval set. Commits can be reviewed separately. **Is this a breaking change?** Yes. **Link to tracking Issue:** n/a. **Testing:** Updated unit tests. --- .../datadogexporter/internal/metrics/utils.go | 24 ++++-- .../internal/metrics/utils_test.go | 4 +- .../datadogexporter/metrics_translator.go | 10 +-- .../metrics_translator_test.go | 86 +++++++++++++++---- 4 files changed, 93 insertions(+), 31 deletions(-) diff --git a/exporter/datadogexporter/internal/metrics/utils.go b/exporter/datadogexporter/internal/metrics/utils.go index c92fce74a5b9..6541add1ca23 100644 --- a/exporter/datadogexporter/internal/metrics/utils.go +++ b/exporter/datadogexporter/internal/metrics/utils.go @@ -24,12 +24,14 @@ import ( "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datadogexporter/config" ) +type MetricDataType string + const ( // Gauge is the Datadog Gauge metric type - Gauge string = "gauge" + Gauge MetricDataType = "gauge" // Count is the Datadog Count metric type - Count string = "count" - otelNamespacePrefix string = "otel" + Count MetricDataType = "count" + otelNamespacePrefix string = "otel" ) // newMetric creates a new Datadog metric given a name, a Unix nanoseconds timestamp @@ -47,20 +49,24 @@ func newMetric(name string, ts uint64, value float64, tags []string) datadog.Met return metric } +// NewMetric creates a new Datadog metric given a name, a type, a Unix nanoseconds timestamp +// a value and a slice of tags +func NewMetric(name string, dt MetricDataType, ts uint64, value float64, tags []string) datadog.Metric { + metric := newMetric(name, ts, value, tags) + metric.SetType(string(dt)) + return metric +} + // NewGauge creates a new Datadog Gauge metric given a name, a Unix nanoseconds timestamp // a value and a slice of tags func NewGauge(name string, ts uint64, value float64, tags []string) datadog.Metric { - gauge := newMetric(name, ts, value, tags) - gauge.SetType(Gauge) - return gauge + return NewMetric(name, Gauge, ts, value, tags) } // NewCount creates a new Datadog count metric given a name, a Unix nanoseconds timestamp // a value and a slice of tags func NewCount(name string, ts uint64, value float64, tags []string) datadog.Metric { - count := newMetric(name, ts, value, tags) - count.SetType(Count) - return count + return NewMetric(name, Count, ts, value, tags) } // DefaultMetrics creates built-in metrics to report that an exporter is running diff --git a/exporter/datadogexporter/internal/metrics/utils_test.go b/exporter/datadogexporter/internal/metrics/utils_test.go index cbe72c1532d5..da54ea81c2cf 100644 --- a/exporter/datadogexporter/internal/metrics/utils_test.go +++ b/exporter/datadogexporter/internal/metrics/utils_test.go @@ -49,10 +49,10 @@ func TestNewType(t *testing.T) { tags := []string{"tag:value"} gauge := NewGauge(name, ts, value, tags) - assert.Equal(t, gauge.GetType(), Gauge) + assert.Equal(t, gauge.GetType(), string(Gauge)) count := NewCount(name, ts, value, tags) - assert.Equal(t, count.GetType(), Count) + assert.Equal(t, count.GetType(), string(Count)) } diff --git a/exporter/datadogexporter/metrics_translator.go b/exporter/datadogexporter/metrics_translator.go index 235f67242db2..79214f95cec5 100644 --- a/exporter/datadogexporter/metrics_translator.go +++ b/exporter/datadogexporter/metrics_translator.go @@ -68,7 +68,7 @@ func metricDimensionsToMapKey(name string, tags []string) string { } // mapNumberMetrics maps double datapoints into Datadog metrics -func mapNumberMetrics(name string, slice pdata.NumberDataPointSlice, attrTags []string) []datadog.Metric { +func mapNumberMetrics(name string, dt metrics.MetricDataType, slice pdata.NumberDataPointSlice, attrTags []string) []datadog.Metric { ms := make([]datadog.Metric, 0, slice.Len()) for i := 0; i < slice.Len(); i++ { p := slice.At(i) @@ -82,7 +82,7 @@ func mapNumberMetrics(name string, slice pdata.NumberDataPointSlice, attrTags [] val = float64(p.IntVal()) } ms = append(ms, - metrics.NewGauge(name, uint64(p.Timestamp()), val, tags), + metrics.NewMetric(name, dt, uint64(p.Timestamp()), val, tags), ) } return ms @@ -257,17 +257,17 @@ func mapMetrics(logger *zap.Logger, cfg config.MetricsConfig, prevPts *ttlmap.TT var datapoints []datadog.Metric switch md.DataType() { case pdata.MetricDataTypeGauge: - datapoints = mapNumberMetrics(md.Name(), md.Gauge().DataPoints(), attributeTags) + datapoints = mapNumberMetrics(md.Name(), metrics.Gauge, md.Gauge().DataPoints(), attributeTags) case pdata.MetricDataTypeSum: switch md.Sum().AggregationTemporality() { case pdata.AggregationTemporalityCumulative: if cfg.SendMonotonic && isCumulativeMonotonic(md) { datapoints = mapNumberMonotonicMetrics(md.Name(), prevPts, md.Sum().DataPoints(), attributeTags) } else { - datapoints = mapNumberMetrics(md.Name(), md.Sum().DataPoints(), attributeTags) + datapoints = mapNumberMetrics(md.Name(), metrics.Gauge, md.Sum().DataPoints(), attributeTags) } case pdata.AggregationTemporalityDelta: - datapoints = mapNumberMetrics(md.Name(), md.Sum().DataPoints(), attributeTags) + datapoints = mapNumberMetrics(md.Name(), metrics.Count, md.Sum().DataPoints(), attributeTags) default: // pdata.AggregationTemporalityUnspecified or any other not supported type logger.Debug("Unknown or unsupported aggregation temporality", zap.String("metric name", md.Name()), diff --git a/exporter/datadogexporter/metrics_translator_test.go b/exporter/datadogexporter/metrics_translator_test.go index 804dce6a90a0..cefe4675fee8 100644 --- a/exporter/datadogexporter/metrics_translator_test.go +++ b/exporter/datadogexporter/metrics_translator_test.go @@ -42,7 +42,7 @@ func TestMetricValue(t *testing.T) { ) metric := metrics.NewGauge(name, ts, value, tags) - assert.Equal(t, metrics.Gauge, metric.GetType()) + assert.Equal(t, string(metrics.Gauge), metric.GetType()) assert.Equal(t, tags, metric.Tags) } @@ -133,13 +133,18 @@ func TestMapIntMetrics(t *testing.T) { point.SetTimestamp(ts) assert.ElementsMatch(t, - mapNumberMetrics("int64.test", slice, []string{}), + mapNumberMetrics("int64.test", metrics.Gauge, slice, []string{}), []datadog.Metric{metrics.NewGauge("int64.test", uint64(ts), 17, []string{})}, ) + assert.ElementsMatch(t, + mapNumberMetrics("int64.delta.test", metrics.Count, slice, []string{}), + []datadog.Metric{metrics.NewCount("int64.delta.test", uint64(ts), 17, []string{})}, + ) + // With attribute tags assert.ElementsMatch(t, - mapNumberMetrics("int64.test", slice, []string{"attribute_tag:attribute_value"}), + mapNumberMetrics("int64.test", metrics.Gauge, slice, []string{"attribute_tag:attribute_value"}), []datadog.Metric{metrics.NewGauge("int64.test", uint64(ts), 17, []string{"attribute_tag:attribute_value"})}, ) } @@ -152,13 +157,18 @@ func TestMapDoubleMetrics(t *testing.T) { point.SetTimestamp(ts) assert.ElementsMatch(t, - mapNumberMetrics("float64.test", slice, []string{}), + mapNumberMetrics("float64.test", metrics.Gauge, slice, []string{}), []datadog.Metric{metrics.NewGauge("float64.test", uint64(ts), math.Pi, []string{})}, ) + assert.ElementsMatch(t, + mapNumberMetrics("float64.delta.test", metrics.Count, slice, []string{}), + []datadog.Metric{metrics.NewCount("float64.delta.test", uint64(ts), math.Pi, []string{})}, + ) + // With attribute tags assert.ElementsMatch(t, - mapNumberMetrics("float64.test", slice, []string{"attribute_tag:attribute_value"}), + mapNumberMetrics("float64.test", metrics.Gauge, slice, []string{"attribute_tag:attribute_value"}), []datadog.Metric{metrics.NewGauge("float64.test", uint64(ts), math.Pi, []string{"attribute_tag:attribute_value"})}, ) } @@ -640,7 +650,7 @@ func createTestMetrics() pdata.Metrics { // Int Sum (delta) met = metricsArray.AppendEmpty() - met.SetName("int.sum") + met.SetName("int.delta.sum") met.SetDataType(pdata.MetricDataTypeSum) met.Sum().SetAggregationTemporality(pdata.AggregationTemporalityDelta) dpsInt = met.Sum().DataPoints() @@ -650,7 +660,27 @@ func createTestMetrics() pdata.Metrics { // Double Sum (delta) met = metricsArray.AppendEmpty() - met.SetName("double.sum") + met.SetName("double.delta.sum") + met.SetDataType(pdata.MetricDataTypeSum) + met.Sum().SetAggregationTemporality(pdata.AggregationTemporalityDelta) + dpsDouble = met.Sum().DataPoints() + dpDouble = dpsDouble.AppendEmpty() + dpDouble.SetTimestamp(seconds(0)) + dpDouble.SetDoubleVal(math.E) + + // Int Sum (delta monotonic) + met = metricsArray.AppendEmpty() + met.SetName("int.delta.monotonic.sum") + met.SetDataType(pdata.MetricDataTypeSum) + met.Sum().SetAggregationTemporality(pdata.AggregationTemporalityDelta) + dpsInt = met.Sum().DataPoints() + dpInt = dpsInt.AppendEmpty() + dpInt.SetTimestamp(seconds(0)) + dpInt.SetIntVal(2) + + // Double Sum (delta monotonic) + met = metricsArray.AppendEmpty() + met.SetName("double.delta.monotonic.sum") met.SetDataType(pdata.MetricDataTypeSum) met.Sum().SetAggregationTemporality(pdata.AggregationTemporalityDelta) dpsDouble = met.Sum().DataPoints() @@ -674,6 +704,28 @@ func createTestMetrics() pdata.Metrics { met.SetName("int.cumulative.sum") met.SetDataType(pdata.MetricDataTypeSum) met.Sum().SetAggregationTemporality(pdata.AggregationTemporalityCumulative) + dpsInt = met.Sum().DataPoints() + dpsInt.EnsureCapacity(2) + dpInt = dpsInt.AppendEmpty() + dpInt.SetTimestamp(seconds(0)) + dpInt.SetIntVal(4) + + // Double Sum (cumulative) + met = metricsArray.AppendEmpty() + met.SetName("double.cumulative.sum") + met.SetDataType(pdata.MetricDataTypeSum) + met.Sum().SetAggregationTemporality(pdata.AggregationTemporalityCumulative) + dpsDouble = met.Sum().DataPoints() + dpsDouble.EnsureCapacity(2) + dpDouble = dpsDouble.AppendEmpty() + dpDouble.SetTimestamp(seconds(0)) + dpDouble.SetDoubleVal(4) + + // Int Sum (cumulative monotonic) + met = metricsArray.AppendEmpty() + met.SetName("int.cumulative.monotonic.sum") + met.SetDataType(pdata.MetricDataTypeSum) + met.Sum().SetAggregationTemporality(pdata.AggregationTemporalityCumulative) met.Sum().SetIsMonotonic(true) dpsInt = met.Sum().DataPoints() dpsInt.EnsureCapacity(2) @@ -684,9 +736,9 @@ func createTestMetrics() pdata.Metrics { dpInt.SetTimestamp(seconds(2)) dpInt.SetIntVal(7) - // Double Sum (cumulative) + // Double Sum (cumulative monotonic) met = metricsArray.AppendEmpty() - met.SetName("double.cumulative.sum") + met.SetName("double.cumulative.monotonic.sum") met.SetDataType(pdata.MetricDataTypeSum) met.Sum().SetAggregationTemporality(pdata.AggregationTemporalityCumulative) met.Sum().SetIsMonotonic(true) @@ -724,8 +776,8 @@ func testGauge(name string, val float64) datadog.Metric { return m } -func testCount(name string, val float64) datadog.Metric { - m := metrics.NewCount(name, 2*1e9, val, []string{}) +func testCount(name string, val float64, seconds uint64) datadog.Metric { + m := metrics.NewCount(name, seconds*1e9, val, []string{}) m.SetHost(testHostname) return m } @@ -746,14 +798,18 @@ func TestMapMetrics(t *testing.T) { assert.ElementsMatch(t, filtered, []datadog.Metric{ testGauge("int.gauge", 1), testGauge("double.gauge", math.Pi), - testGauge("int.sum", 2), - testGauge("double.sum", math.E), + testCount("int.delta.sum", 2, 0), + testCount("double.delta.sum", math.E, 0), + testCount("int.delta.monotonic.sum", 2, 0), + testCount("double.delta.monotonic.sum", math.E, 0), testGauge("double.histogram.sum", math.Phi), testGauge("double.histogram.count", 20), testGauge("summary.sum", 10_000), testGauge("summary.count", 100), - testCount("int.cumulative.sum", 3), - testCount("double.cumulative.sum", math.Pi), + testGauge("int.cumulative.sum", 4), + testGauge("double.cumulative.sum", 4), + testCount("int.cumulative.monotonic.sum", 3, 2), + testCount("double.cumulative.monotonic.sum", math.Pi, 2), }) // One metric type was unknown or unsupported