From 9841f48eae5a4afe58857ac0ebbe29d3e124d135 Mon Sep 17 00:00:00 2001 From: Carson Ip Date: Thu, 11 Jul 2024 19:22:48 +0100 Subject: [PATCH 01/10] [exporter/elasticsearch] Add metric histogram support --- .../data_stream_router.go | 3 +- exporter/elasticsearchexporter/exporter.go | 76 ++++++++++++++----- .../elasticsearchexporter/exporter_test.go | 1 + exporter/elasticsearchexporter/model.go | 66 ++++++++++++++-- exporter/elasticsearchexporter/model_test.go | 7 +- exporter/elasticsearchexporter/utils_test.go | 4 +- 6 files changed, 128 insertions(+), 29 deletions(-) diff --git a/exporter/elasticsearchexporter/data_stream_router.go b/exporter/elasticsearchexporter/data_stream_router.go index 0368f6a1b958..b95a66a913a2 100644 --- a/exporter/elasticsearchexporter/data_stream_router.go +++ b/exporter/elasticsearchexporter/data_stream_router.go @@ -8,7 +8,6 @@ import ( "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/plog" - "go.opentelemetry.io/collector/pdata/pmetric" "go.opentelemetry.io/collector/pdata/ptrace" ) @@ -60,7 +59,7 @@ func routeLogRecord( // routeDataPoint returns the name of the index to send the data point to according to data stream routing attributes. // This function may mutate record attributes. func routeDataPoint( - dataPoint pmetric.NumberDataPoint, + dataPoint dataPoint, scope pcommon.InstrumentationScope, resource pcommon.Resource, fIndex string, diff --git a/exporter/elasticsearchexporter/exporter.go b/exporter/elasticsearchexporter/exporter.go index 6cb64da0983d..9bf5deaa8873 100644 --- a/exporter/elasticsearchexporter/exporter.go +++ b/exporter/elasticsearchexporter/exporter.go @@ -157,27 +157,64 @@ func (e *elasticsearchExporter) pushMetricsData( for k := 0; k < scopeMetrics.Metrics().Len(); k++ { metric := scopeMetrics.Metrics().At(k) - // We only support Sum and Gauge metrics at the moment. - var dataPoints pmetric.NumberDataPointSlice - switch metric.Type() { - case pmetric.MetricTypeSum: - dataPoints = metric.Sum().DataPoints() - case pmetric.MetricTypeGauge: - dataPoints = metric.Gauge().DataPoints() - } - - for l := 0; l < dataPoints.Len(); l++ { - dataPoint := dataPoints.At(l) - fIndex, err := e.getMetricDataPointIndex(resource, scope, dataPoint) + upsertDataPoint := func(dp dataPoint, dpValue pcommon.Value) error { + fIndex, err := e.getMetricDataPointIndex(resource, scope, dp) if err != nil { - errs = append(errs, err) - continue + return err } if _, ok := resourceDocs[fIndex]; !ok { resourceDocs[fIndex] = make(map[uint32]objmodel.Document) } - if err := e.model.upsertMetricDataPoint(resourceDocs[fIndex], resource, scope, metric, dataPoint); err != nil { - errs = append(errs, err) + + if err = e.model.upsertMetricDataPointValue(resourceDocs[fIndex], resource, scope, metric, dp, dpValue); err != nil { + return err + } + return nil + } + + // TODO: support exponential histogram + switch metric.Type() { + case pmetric.MetricTypeSum: + dps := metric.Sum().DataPoints() + for l := 0; l < dps.Len(); l++ { + dp := dps.At(l) + val, err := numberToValue(dp) + if err != nil { + errs = append(errs, err) + continue + } + if err := upsertDataPoint(dp, val); err != nil { + errs = append(errs, err) + continue + } + } + case pmetric.MetricTypeGauge: + dps := metric.Gauge().DataPoints() + for l := 0; l < dps.Len(); l++ { + dp := dps.At(l) + val, err := numberToValue(dp) + if err != nil { + errs = append(errs, err) + continue + } + if err := upsertDataPoint(dp, val); err != nil { + errs = append(errs, err) + continue + } + } + case pmetric.MetricTypeHistogram: + dps := metric.Histogram().DataPoints() + for l := 0; l < dps.Len(); l++ { + dp := dps.At(l) + val, err := histogramToValue(dp) + if err != nil { + errs = append(errs, err) + continue + } + if err := upsertDataPoint(dp, val); err != nil { + errs = append(errs, err) + continue + } } } } @@ -208,10 +245,15 @@ func (e *elasticsearchExporter) pushMetricsData( return errors.Join(errs...) } +type dataPoint interface { + Timestamp() pcommon.Timestamp + Attributes() pcommon.Map +} + func (e *elasticsearchExporter) getMetricDataPointIndex( resource pcommon.Resource, scope pcommon.InstrumentationScope, - dataPoint pmetric.NumberDataPoint, + dataPoint dataPoint, ) (string, error) { fIndex := e.index if e.dynamicIndex { diff --git a/exporter/elasticsearchexporter/exporter_test.go b/exporter/elasticsearchexporter/exporter_test.go index 754cfaa4675f..92217b8c9015 100644 --- a/exporter/elasticsearchexporter/exporter_test.go +++ b/exporter/elasticsearchexporter/exporter_test.go @@ -495,6 +495,7 @@ func TestExporterMetrics(t *testing.T) { }, ) metrics.ResourceMetrics().At(0).ScopeMetrics().At(0).Metrics().At(0).SetName("my.metric") + metrics.ResourceMetrics().At(0).ScopeMetrics().At(0).Metrics().At(0).SetEmptySum().DataPoints().AppendEmpty().SetIntValue(0) mustSendMetrics(t, exporter, metrics) rec.WaitItems(1) diff --git a/exporter/elasticsearchexporter/model.go b/exporter/elasticsearchexporter/model.go index ccf76b5afdf6..36961ae18a0d 100644 --- a/exporter/elasticsearchexporter/model.go +++ b/exporter/elasticsearchexporter/model.go @@ -7,6 +7,7 @@ import ( "bytes" "encoding/binary" "encoding/json" + "errors" "fmt" "hash" "hash/fnv" @@ -65,7 +66,7 @@ var resourceAttrsToPreserve = map[string]bool{ type mappingModel interface { encodeLog(pcommon.Resource, plog.LogRecord, pcommon.InstrumentationScope) ([]byte, error) encodeSpan(pcommon.Resource, ptrace.Span, pcommon.InstrumentationScope) ([]byte, error) - upsertMetricDataPoint(map[uint32]objmodel.Document, pcommon.Resource, pcommon.InstrumentationScope, pmetric.Metric, pmetric.NumberDataPoint) error + upsertMetricDataPointValue(map[uint32]objmodel.Document, pcommon.Resource, pcommon.InstrumentationScope, pmetric.Metric, dataPoint, pcommon.Value) error encodeDocument(objmodel.Document) ([]byte, error) } @@ -185,7 +186,7 @@ func (m *encodeModel) encodeDocument(document objmodel.Document) ([]byte, error) return buf.Bytes(), nil } -func (m *encodeModel) upsertMetricDataPoint(documents map[uint32]objmodel.Document, resource pcommon.Resource, _ pcommon.InstrumentationScope, metric pmetric.Metric, dp pmetric.NumberDataPoint) error { +func (m *encodeModel) upsertMetricDataPointValue(documents map[uint32]objmodel.Document, resource pcommon.Resource, _ pcommon.InstrumentationScope, metric pmetric.Metric, dp dataPoint, value pcommon.Value) error { hash := metricHash(dp.Timestamp(), dp.Attributes()) var ( document objmodel.Document @@ -197,15 +198,66 @@ func (m *encodeModel) upsertMetricDataPoint(documents map[uint32]objmodel.Docume document.AddAttributes("", dp.Attributes()) } + document.AddAttribute(metric.Name(), value) + + documents[hash] = document + return nil +} + +func histogramToValue(dp pmetric.HistogramDataPoint) (pcommon.Value, error) { + bucketCounts := dp.BucketCounts() + explicitBounds := dp.ExplicitBounds() + if bucketCounts.Len() != explicitBounds.Len()+1 || explicitBounds.Len() == 0 { + return pcommon.Value{}, errors.New("error in histogram") + } + + vm := pcommon.NewValueMap() + m := vm.Map() + counts := m.PutEmptySlice("counts") + values := m.PutEmptySlice("values") + + values.EnsureCapacity(bucketCounts.Len()) + counts.EnsureCapacity(bucketCounts.Len()) + for i := 0; i < bucketCounts.Len(); i++ { + count := bucketCounts.At(i) + if count == 0 { + continue + } + + var value float64 + switch i { + // (-infinity, explicit_bounds[i]] + case 0: + value = explicitBounds.At(i) + if value > 0 { + value /= 2 + } + + // (explicit_bounds[i], +infinity) + case bucketCounts.Len() - 1: + value = explicitBounds.At(i - 1) + + // [explicit_bounds[i-1], explicit_bounds[i]) + default: + // Use the midpoint between the boundaries. + value = explicitBounds.At(i-1) + (explicitBounds.At(i)-explicitBounds.At(i-1))/2.0 + } + + counts.AppendEmpty().SetInt(int64(count)) + values.AppendEmpty().SetDouble(value) + } + + return vm, nil +} + +func numberToValue(dp pmetric.NumberDataPoint) (pcommon.Value, error) { switch dp.ValueType() { case pmetric.NumberDataPointValueTypeDouble: - document.AddAttribute(metric.Name(), pcommon.NewValueDouble(dp.DoubleValue())) + return pcommon.NewValueDouble(dp.DoubleValue()), nil case pmetric.NumberDataPointValueTypeInt: - document.AddAttribute(metric.Name(), pcommon.NewValueInt(dp.IntValue())) + return pcommon.NewValueInt(dp.IntValue()), nil } - - documents[hash] = document - return nil + return pcommon.Value{}, errors.New("invalid number data point") } func (m *encodeModel) encodeSpan(resource pcommon.Resource, span ptrace.Span, scope pcommon.InstrumentationScope) ([]byte, error) { diff --git a/exporter/elasticsearchexporter/model_test.go b/exporter/elasticsearchexporter/model_test.go index d3784e5081f1..c0be9a9c8fad 100644 --- a/exporter/elasticsearchexporter/model_test.go +++ b/exporter/elasticsearchexporter/model_test.go @@ -97,11 +97,14 @@ func TestEncodeMetric(t *testing.T) { var docsBytes [][]byte for i := 0; i < metrics.ResourceMetrics().At(0).ScopeMetrics().At(0).Metrics().At(0).Sum().DataPoints().Len(); i++ { - err := model.upsertMetricDataPoint(docs, + val, err := numberToValue(metrics.ResourceMetrics().At(0).ScopeMetrics().At(0).Metrics().At(0).Sum().DataPoints().At(i)) + require.NoError(t, err) + err = model.upsertMetricDataPointValue(docs, metrics.ResourceMetrics().At(0).Resource(), metrics.ResourceMetrics().At(0).ScopeMetrics().At(0).Scope(), metrics.ResourceMetrics().At(0).ScopeMetrics().At(0).Metrics().At(0), - metrics.ResourceMetrics().At(0).ScopeMetrics().At(0).Metrics().At(0).Sum().DataPoints().At(i)) + metrics.ResourceMetrics().At(0).ScopeMetrics().At(0).Metrics().At(0).Sum().DataPoints().At(i), + val) require.NoError(t, err) } diff --git a/exporter/elasticsearchexporter/utils_test.go b/exporter/elasticsearchexporter/utils_test.go index f57f16272c24..e2c55a47609a 100644 --- a/exporter/elasticsearchexporter/utils_test.go +++ b/exporter/elasticsearchexporter/utils_test.go @@ -266,7 +266,9 @@ func newMetricsWithAttributeAndResourceMap(attrMp map[string]string, resMp map[s resourceMetrics := metrics.ResourceMetrics().AppendEmpty() fillResourceAttributeMap(resourceMetrics.Resource().Attributes(), resMp) - fillResourceAttributeMap(resourceMetrics.ScopeMetrics().AppendEmpty().Metrics().AppendEmpty().SetEmptySum().DataPoints().AppendEmpty().Attributes(), attrMp) + dp := resourceMetrics.ScopeMetrics().AppendEmpty().Metrics().AppendEmpty().SetEmptySum().DataPoints().AppendEmpty() + dp.SetIntValue(0) + fillResourceAttributeMap(dp.Attributes(), attrMp) return metrics } From 58179c2822cbcecf9504399fdac7192f293e5873 Mon Sep 17 00:00:00 2001 From: Carson Ip Date: Thu, 11 Jul 2024 20:06:10 +0100 Subject: [PATCH 02/10] Add test --- .../elasticsearchexporter/exporter_test.go | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/exporter/elasticsearchexporter/exporter_test.go b/exporter/elasticsearchexporter/exporter_test.go index 92217b8c9015..0490183a42c2 100644 --- a/exporter/elasticsearchexporter/exporter_test.go +++ b/exporter/elasticsearchexporter/exporter_test.go @@ -634,6 +634,50 @@ func TestExporterMetrics(t *testing.T) { assertItemsEqual(t, expected, rec.Items(), false) }) + + t.Run("publish histogram", func(t *testing.T) { + rec := newBulkRecorder() + server := newESTestServer(t, func(docs []itemRequest) ([]itemResponse, error) { + rec.Record(docs) + return itemsAllOK(docs) + }) + + exporter := newTestMetricsExporter(t, server.URL, func(cfg *Config) { + cfg.Mapping.Mode = "ecs" + }) + + metrics := pmetric.NewMetrics() + resourceMetrics := metrics.ResourceMetrics().AppendEmpty() + scopeA := resourceMetrics.ScopeMetrics().AppendEmpty() + metricSlice := scopeA.Metrics() + fooMetric := metricSlice.AppendEmpty() + fooMetric.SetName("metric.foo") + fooDps := fooMetric.SetEmptyHistogram().DataPoints() + fooDp := fooDps.AppendEmpty() + fooDp.ExplicitBounds().FromRaw([]float64{1.0, 2.0, 3.0}) + fooDp.BucketCounts().FromRaw([]uint64{1, 2, 3, 4}) + fooOtherDp := fooDps.AppendEmpty() + fooOtherDp.SetTimestamp(pcommon.NewTimestampFromTime(time.Unix(3600, 0))) + fooOtherDp.ExplicitBounds().FromRaw([]float64{4.0, 5.0, 6.0}) + fooOtherDp.BucketCounts().FromRaw([]uint64{4, 5, 6, 7}) + + mustSendMetrics(t, exporter, metrics) + + rec.WaitItems(2) + + expected := []itemRequest{ + { + Action: []byte(`{"create":{"_index":"metrics-generic-default"}}`), + Document: []byte(`{"@timestamp":"1970-01-01T00:00:00.000000000Z","data_stream":{"dataset":"generic","namespace":"default","type":"metrics"},"metric":{"foo":{"counts":[1,2,3,4],"values":[0.5,1.5,2.5,3]}}}`), + }, + { + Action: []byte(`{"create":{"_index":"metrics-generic-default"}}`), + Document: []byte(`{"@timestamp":"1970-01-01T01:00:00.000000000Z","data_stream":{"dataset":"generic","namespace":"default","type":"metrics"},"metric":{"foo":{"counts":[4,5,6,7],"values":[2,4.5,5.5,6]}}}`), + }, + } + + assertItemsEqual(t, expected, rec.Items(), false) + }) } func TestExporterTraces(t *testing.T) { From 43f2c5bc78fc6ea908622e063af7cc00ca31ebb2 Mon Sep 17 00:00:00 2001 From: Carson Ip Date: Thu, 11 Jul 2024 20:07:15 +0100 Subject: [PATCH 03/10] Add changelog --- ...rchexporter_metrics-histogram-support.yaml | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .chloggen/elasticsearchexporter_metrics-histogram-support.yaml diff --git a/.chloggen/elasticsearchexporter_metrics-histogram-support.yaml b/.chloggen/elasticsearchexporter_metrics-histogram-support.yaml new file mode 100644 index 000000000000..13a875da423d --- /dev/null +++ b/.chloggen/elasticsearchexporter_metrics-histogram-support.yaml @@ -0,0 +1,27 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: enhancement + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: elasticsearchexporter + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Add histogram support to metrics + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [34045] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: + +# If your change doesn't affect end users or the exported elements of any package, +# you should instead start your pull request title with [chore] or use the "Skip Changelog" label. +# Optional: The change log or logs in which this entry should be included. +# e.g. '[user]' or '[user, api]' +# Include 'user' if the change is relevant to end users. +# Include 'api' if there is a change to a library API. +# Default: '[user]' +change_logs: [] From 244d96b25872744e67adf7cfc21c8ee8d1d8f509 Mon Sep 17 00:00:00 2001 From: Carson Ip Date: Fri, 12 Jul 2024 11:14:49 +0100 Subject: [PATCH 04/10] Refactor invalid data point handling --- exporter/elasticsearchexporter/model.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/exporter/elasticsearchexporter/model.go b/exporter/elasticsearchexporter/model.go index 36961ae18a0d..8f7973a9d912 100644 --- a/exporter/elasticsearchexporter/model.go +++ b/exporter/elasticsearchexporter/model.go @@ -208,7 +208,7 @@ func histogramToValue(dp pmetric.HistogramDataPoint) (pcommon.Value, error) { bucketCounts := dp.BucketCounts() explicitBounds := dp.ExplicitBounds() if bucketCounts.Len() != explicitBounds.Len()+1 || explicitBounds.Len() == 0 { - return pcommon.Value{}, errors.New("error in histogram") + return pcommon.Value{}, errors.New("invalid histogram data point") } vm := pcommon.NewValueMap() @@ -250,14 +250,20 @@ func histogramToValue(dp pmetric.HistogramDataPoint) (pcommon.Value, error) { return vm, nil } +var invalidNumberDataPointErr = errors.New("invalid number data point") + func numberToValue(dp pmetric.NumberDataPoint) (pcommon.Value, error) { switch dp.ValueType() { case pmetric.NumberDataPointValueTypeDouble: - return pcommon.NewValueDouble(dp.DoubleValue()), nil + value := dp.DoubleValue() + if math.IsNaN(value) || math.IsInf(value, 0) { + return pcommon.Value{}, invalidNumberDataPointErr + } + return pcommon.NewValueDouble(value), nil case pmetric.NumberDataPointValueTypeInt: return pcommon.NewValueInt(dp.IntValue()), nil } - return pcommon.Value{}, errors.New("invalid number data point") + return pcommon.Value{}, invalidNumberDataPointErr } func (m *encodeModel) encodeSpan(resource pcommon.Resource, span ptrace.Span, scope pcommon.InstrumentationScope) ([]byte, error) { From 9567252cbab4ccde98dd6678677605490ff6513c Mon Sep 17 00:00:00 2001 From: Carson Ip Date: Fri, 12 Jul 2024 11:55:17 +0100 Subject: [PATCH 05/10] Add test for invalid dp --- .../elasticsearchexporter/exporter_test.go | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/exporter/elasticsearchexporter/exporter_test.go b/exporter/elasticsearchexporter/exporter_test.go index 0490183a42c2..eae62957f680 100644 --- a/exporter/elasticsearchexporter/exporter_test.go +++ b/exporter/elasticsearchexporter/exporter_test.go @@ -8,6 +8,7 @@ import ( "encoding/json" "errors" "fmt" + "math" "net/http" "runtime" "sync" @@ -678,6 +679,59 @@ func TestExporterMetrics(t *testing.T) { assertItemsEqual(t, expected, rec.Items(), false) }) + + t.Run("publish only valid data points", func(t *testing.T) { + rec := newBulkRecorder() + server := newESTestServer(t, func(docs []itemRequest) ([]itemResponse, error) { + rec.Record(docs) + return itemsAllOK(docs) + }) + + exporter := newTestMetricsExporter(t, server.URL, func(cfg *Config) { + cfg.Mapping.Mode = "ecs" + }) + + metrics := pmetric.NewMetrics() + resourceMetrics := metrics.ResourceMetrics().AppendEmpty() + scopeA := resourceMetrics.ScopeMetrics().AppendEmpty() + metricSlice := scopeA.Metrics() + fooMetric := metricSlice.AppendEmpty() + fooMetric.SetName("metric.foo") + fooDps := fooMetric.SetEmptyHistogram().DataPoints() + fooDp := fooDps.AppendEmpty() + fooDp.ExplicitBounds().FromRaw([]float64{1.0, 2.0, 3.0}) + fooDp.BucketCounts().FromRaw([]uint64{}) + fooOtherDp := fooDps.AppendEmpty() + fooOtherDp.SetTimestamp(pcommon.NewTimestampFromTime(time.Unix(3600, 0))) + fooOtherDp.ExplicitBounds().FromRaw([]float64{4.0, 5.0, 6.0}) + fooOtherDp.BucketCounts().FromRaw([]uint64{4, 5, 6, 7}) + barMetric := metricSlice.AppendEmpty() + barMetric.SetName("metric.bar") + barDps := barMetric.SetEmptySum().DataPoints() + barDp := barDps.AppendEmpty() + barDp.SetDoubleValue(math.Inf(1)) + barOtherDp := barDps.AppendEmpty() + barOtherDp.SetDoubleValue(1.0) + + err := exporter.ConsumeMetrics(context.Background(), metrics) + require.ErrorContains(t, err, "invalid histogram data point") + require.ErrorContains(t, err, "invalid number data point") + + rec.WaitItems(2) + + expected := []itemRequest{ + { + Action: []byte(`{"create":{"_index":"metrics-generic-default"}}`), + Document: []byte(`{"@timestamp":"1970-01-01T00:00:00.000000000Z","data_stream":{"dataset":"generic","namespace":"default","type":"metrics"},"metric":{"bar":1}}`), + }, + { + Action: []byte(`{"create":{"_index":"metrics-generic-default"}}`), + Document: []byte(`{"@timestamp":"1970-01-01T01:00:00.000000000Z","data_stream":{"dataset":"generic","namespace":"default","type":"metrics"},"metric":{"foo":{"counts":[4,5,6,7],"values":[2,4.5,5.5,6]}}}`), + }, + } + + assertItemsEqual(t, expected, rec.Items(), false) + }) } func TestExporterTraces(t *testing.T) { From 646c7a045bd2c775d1bb4039c7e306e2335eaa54 Mon Sep 17 00:00:00 2001 From: Carson Ip Date: Fri, 12 Jul 2024 11:56:34 +0100 Subject: [PATCH 06/10] Move dp interface --- exporter/elasticsearchexporter/exporter.go | 5 ----- exporter/elasticsearchexporter/model.go | 5 +++++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/exporter/elasticsearchexporter/exporter.go b/exporter/elasticsearchexporter/exporter.go index 9bf5deaa8873..a9ecf11c3679 100644 --- a/exporter/elasticsearchexporter/exporter.go +++ b/exporter/elasticsearchexporter/exporter.go @@ -245,11 +245,6 @@ func (e *elasticsearchExporter) pushMetricsData( return errors.Join(errs...) } -type dataPoint interface { - Timestamp() pcommon.Timestamp - Attributes() pcommon.Map -} - func (e *elasticsearchExporter) getMetricDataPointIndex( resource pcommon.Resource, scope pcommon.InstrumentationScope, diff --git a/exporter/elasticsearchexporter/model.go b/exporter/elasticsearchexporter/model.go index 8f7973a9d912..c834b7593660 100644 --- a/exporter/elasticsearchexporter/model.go +++ b/exporter/elasticsearchexporter/model.go @@ -82,6 +82,11 @@ type encodeModel struct { mode MappingMode } +type dataPoint interface { + Timestamp() pcommon.Timestamp + Attributes() pcommon.Map +} + const ( traceIDField = "traceID" spanIDField = "spanID" From 074a40634f2f92f7e0bfcc4e5a349df241f3ed5d Mon Sep 17 00:00:00 2001 From: Carson Ip Date: Fri, 12 Jul 2024 12:03:33 +0100 Subject: [PATCH 07/10] Add link --- exporter/elasticsearchexporter/model.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/exporter/elasticsearchexporter/model.go b/exporter/elasticsearchexporter/model.go index c834b7593660..862ebf8bce63 100644 --- a/exporter/elasticsearchexporter/model.go +++ b/exporter/elasticsearchexporter/model.go @@ -210,6 +210,8 @@ func (m *encodeModel) upsertMetricDataPointValue(documents map[uint32]objmodel.D } func histogramToValue(dp pmetric.HistogramDataPoint) (pcommon.Value, error) { + // Histogram conversion function is from + // https://github.com/elastic/apm-data/blob/3b28495c3cbdc0902983134276eb114231730249/input/otlp/metrics.go#L277 bucketCounts := dp.BucketCounts() explicitBounds := dp.ExplicitBounds() if bucketCounts.Len() != explicitBounds.Len()+1 || explicitBounds.Len() == 0 { From c5e2f787869fde500259485f3c017f8029d64dfb Mon Sep 17 00:00:00 2001 From: Carson Ip Date: Fri, 12 Jul 2024 12:37:32 +0100 Subject: [PATCH 08/10] Rename err --- exporter/elasticsearchexporter/model.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/exporter/elasticsearchexporter/model.go b/exporter/elasticsearchexporter/model.go index 862ebf8bce63..3c4d5a6aaf38 100644 --- a/exporter/elasticsearchexporter/model.go +++ b/exporter/elasticsearchexporter/model.go @@ -257,20 +257,20 @@ func histogramToValue(dp pmetric.HistogramDataPoint) (pcommon.Value, error) { return vm, nil } -var invalidNumberDataPointErr = errors.New("invalid number data point") +var errInvalidNumberDataPoint = errors.New("invalid number data point") func numberToValue(dp pmetric.NumberDataPoint) (pcommon.Value, error) { switch dp.ValueType() { case pmetric.NumberDataPointValueTypeDouble: value := dp.DoubleValue() if math.IsNaN(value) || math.IsInf(value, 0) { - return pcommon.Value{}, invalidNumberDataPointErr + return pcommon.Value{}, errInvalidNumberDataPoint } return pcommon.NewValueDouble(value), nil case pmetric.NumberDataPointValueTypeInt: return pcommon.NewValueInt(dp.IntValue()), nil } - return pcommon.Value{}, invalidNumberDataPointErr + return pcommon.Value{}, errInvalidNumberDataPoint } func (m *encodeModel) encodeSpan(resource pcommon.Resource, span ptrace.Span, scope pcommon.InstrumentationScope) ([]byte, error) { From 9840648ff442e8e99d1924f5472ba442ccddc113 Mon Sep 17 00:00:00 2001 From: Carson Ip Date: Tue, 16 Jul 2024 10:00:35 +0100 Subject: [PATCH 09/10] Update .chloggen/elasticsearchexporter_metrics-histogram-support.yaml Co-authored-by: Andrew Wilkins --- .chloggen/elasticsearchexporter_metrics-histogram-support.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.chloggen/elasticsearchexporter_metrics-histogram-support.yaml b/.chloggen/elasticsearchexporter_metrics-histogram-support.yaml index 13a875da423d..eeed9993aba9 100644 --- a/.chloggen/elasticsearchexporter_metrics-histogram-support.yaml +++ b/.chloggen/elasticsearchexporter_metrics-histogram-support.yaml @@ -24,4 +24,4 @@ subtext: # Include 'user' if the change is relevant to end users. # Include 'api' if there is a change to a library API. # Default: '[user]' -change_logs: [] +change_logs: [user] From d4388972e8f369037d68e6e422ac7c9b6e11f4b3 Mon Sep 17 00:00:00 2001 From: Carson Ip Date: Tue, 16 Jul 2024 10:03:01 +0100 Subject: [PATCH 10/10] Mention explicit bounds histogram in changelog --- .chloggen/elasticsearchexporter_metrics-histogram-support.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.chloggen/elasticsearchexporter_metrics-histogram-support.yaml b/.chloggen/elasticsearchexporter_metrics-histogram-support.yaml index eeed9993aba9..d2b7369d1010 100644 --- a/.chloggen/elasticsearchexporter_metrics-histogram-support.yaml +++ b/.chloggen/elasticsearchexporter_metrics-histogram-support.yaml @@ -7,7 +7,7 @@ change_type: enhancement component: elasticsearchexporter # A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). -note: Add histogram support to metrics +note: Add explicit bounds histogram support to metrics # Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. issues: [34045]