diff --git a/.chloggen/span-metrics-add-exemplars-to-sum-metric.yaml b/.chloggen/span-metrics-add-exemplars-to-sum-metric.yaml new file mode 100644 index 000000000000..60ca23a4bc82 --- /dev/null +++ b/.chloggen/span-metrics-add-exemplars-to-sum-metric.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: spanmetricsconnector + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Add exemplars to sum metric + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [27451] + +# (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: [] \ No newline at end of file diff --git a/connector/spanmetricsconnector/connector.go b/connector/spanmetricsconnector/connector.go index 9e0189f1f921..3832dca63997 100644 --- a/connector/spanmetricsconnector/connector.go +++ b/connector/spanmetricsconnector/connector.go @@ -337,6 +337,9 @@ func (p *connectorImp) aggregateMetrics(traces ptrace.Traces) { } // aggregate sums metrics s := sums.GetOrCreate(key, attributes) + if p.config.Exemplars.Enabled && !span.TraceID().IsEmpty() { + s.AddExemplar(span.TraceID(), span.SpanID(), duration) + } s.Add(1) // aggregate events metrics diff --git a/connector/spanmetricsconnector/connector_test.go b/connector/spanmetricsconnector/connector_test.go index 2e3e3c34c7cb..7d75015a07ff 100644 --- a/connector/spanmetricsconnector/connector_test.go +++ b/connector/spanmetricsconnector/connector_test.go @@ -1318,3 +1318,34 @@ func TestSpanMetrics_Events(t *testing.T) { }) } } +func TestExemplarsForSumMetrics(t *testing.T) { + mcon := consumertest.NewNop() + p := newConnectorImp(t, mcon, stringp("defaultNullValue"), explicitHistogramsConfig, enabledExemplarsConfig, cumulative, zaptest.NewLogger(t), nil) + traces := buildSampleTrace() + + // Test + ctx := metadata.NewIncomingContext(context.Background(), nil) + + err := p.ConsumeTraces(ctx, traces) + require.NoError(t, err) + metrics := p.buildMetrics() + + for i := 0; i < metrics.ResourceMetrics().Len(); i++ { + rm := metrics.ResourceMetrics().At(i) + ism := rm.ScopeMetrics() + // Checking all metrics, naming notice: ilmC/mC - C here is for Counter. + for ilmC := 0; ilmC < ism.Len(); ilmC++ { + m := ism.At(ilmC).Metrics() + for mC := 0; mC < m.Len(); mC++ { + metric := m.At(mC) + if metric.Type() == pmetric.MetricTypeSum { + dps := metric.Sum().DataPoints() + for dpi := 0; dpi < dps.Len(); dpi++ { + dp := dps.At(dpi) + assert.Greater(t, dp.Exemplars().Len(), 0) + } + } + } + } + } +} diff --git a/connector/spanmetricsconnector/internal/metrics/metrics.go b/connector/spanmetricsconnector/internal/metrics/metrics.go index de2e0ac954ea..2bae86f4333c 100644 --- a/connector/spanmetricsconnector/internal/metrics/metrics.go +++ b/connector/spanmetricsconnector/internal/metrics/metrics.go @@ -232,6 +232,7 @@ func (h *exponentialHistogram) AddExemplar(traceID pcommon.TraceID, spanID pcomm type Sum struct { attributes pcommon.Map count uint64 + exemplars pmetric.ExemplarSlice } func (s *Sum) Add(value uint64) { @@ -251,12 +252,20 @@ func (m *SumMetrics) GetOrCreate(key Key, attributes pcommon.Map) *Sum { if !ok { s = &Sum{ attributes: attributes, + exemplars: pmetric.NewExemplarSlice(), } m.metrics[key] = s } return s } +func (s *Sum) AddExemplar(traceID pcommon.TraceID, spanID pcommon.SpanID, value float64) { + e := s.exemplars.AppendEmpty() + e.SetTraceID(traceID) + e.SetSpanID(spanID) + e.SetDoubleValue(value) +} + func (m *SumMetrics) BuildMetrics( metric pmetric.Metric, start pcommon.Timestamp, @@ -273,6 +282,10 @@ func (m *SumMetrics) BuildMetrics( dp.SetStartTimestamp(start) dp.SetTimestamp(timestamp) dp.SetIntValue(int64(s.count)) + for i := 0; i < s.exemplars.Len(); i++ { + s.exemplars.At(i).SetTimestamp(timestamp) + } + s.exemplars.CopyTo(dp.Exemplars()) s.attributes.CopyTo(dp.Attributes()) } }