Skip to content

Commit b8362a5

Browse files
shivanthzenatoulmeFrapschenrenovate[bot]opentelemetrybot
authored
[connector/spanmetrics] Fix spanmetrics for child spans (#36718)
<!--Ex. Fixing a bug - Describe the bug and how this fixes the issue. Ex. Adding a feature - Explain what this achieves.--> #### Description Currently, metric start timestamps are associated with the parent span (resource level). This means that all child spans, even those that occur asynchronously or infrequently, inherit the same start timestamp. This can lead to inaccurate data. This merge request proposes moving the start timestamp (and last seen timestamp) from the parent span level to the individual child span (metric) level. This will ensure that each metric has its own accurate start and last seen timestamps, regardless of its relationship to other spans. <!-- Issue number (e.g. #1234) or full URL to issue, if applicable. --> #### Link to tracking issue Fixes #35994 <!--Describe what testing was performed and which tests were added.--> #### Testing <!--Describe the documentation added.--> #### Documentation <!--Please delete paragraphs that you did not use before submitting.--> --------- Co-authored-by: Antoine Toulme <[email protected]> Co-authored-by: Murphy Chen <[email protected]> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: opentelemetrybot <[email protected]> Co-authored-by: Yang Song <[email protected]>
1 parent 35d2454 commit b8362a5

File tree

3 files changed

+67
-40
lines changed

3 files changed

+67
-40
lines changed

.chloggen/fix_spanmetrics.yaml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Use this changelog template to create an entry for release notes.
2+
3+
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
4+
change_type: bug_fix
5+
# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
6+
component: connector/spanmetrics
7+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
8+
note: This change proposes moving the start timestamp (and last seen timestamp) from the resourceMetrics level to the individual metrics level. This will ensure that each metric has its own accurate start and last seen timestamps, regardless of its relationship to other spans.
9+
# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
10+
issues: [35994]
11+
# (Optional) One or more lines of additional information to render under the primary note.
12+
# These lines will be padded with 2 spaces and then inserted directly into the document.
13+
# Use pipe (|) for multiline entries.
14+
subtext:
15+
# If your change doesn't affect end users or the exported elements of any package,
16+
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
17+
# Optional: The change log or logs in which this entry should be included.
18+
# e.g. '[user]' or '[user, api]'
19+
# Include 'user' if the change is relevant to end users.
20+
# Include 'api' if there is a change to a library API.
21+
# Default: '[user]'
22+
change_logs: [user]

connector/spanmetricsconnector/connector.go

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,6 @@ type resourceMetrics struct {
8787
sums metrics.SumMetrics
8888
events metrics.SumMetrics
8989
attributes pcommon.Map
90-
// startTimestamp captures when the first data points for this resource are recorded.
91-
startTimestamp pcommon.Timestamp
9290
// lastSeen captures when the last data points for this resource were recorded.
9391
lastSeen time.Time
9492
}
@@ -281,8 +279,7 @@ func (p *connectorImp) buildMetrics() pmetric.Metrics {
281279
* - For delta metrics: (T1, T2), (T2, T3), (T3, T4) ...
282280
*/
283281
deltaMetricKeys := make(map[metrics.Key]bool)
284-
startTimeGenerator := func(mk metrics.Key) pcommon.Timestamp {
285-
startTime := rawMetrics.startTimestamp
282+
timeStampGenerator := func(mk metrics.Key, startTime pcommon.Timestamp) pcommon.Timestamp {
286283
if p.config.GetAggregationTemporality() == pmetric.AggregationTemporalityDelta {
287284
if lastTimestamp, ok := p.lastDeltaTimestamps.Get(mk); ok {
288285
startTime = lastTimestamp
@@ -301,21 +298,21 @@ func (p *connectorImp) buildMetrics() pmetric.Metrics {
301298
sums := rawMetrics.sums
302299
metric := sm.Metrics().AppendEmpty()
303300
metric.SetName(buildMetricName(metricsNamespace, metricNameCalls))
304-
sums.BuildMetrics(metric, startTimeGenerator, timestamp, p.config.GetAggregationTemporality())
301+
sums.BuildMetrics(metric, timestamp, timeStampGenerator, p.config.GetAggregationTemporality())
305302

306303
if !p.config.Histogram.Disable {
307304
histograms := rawMetrics.histograms
308305
metric = sm.Metrics().AppendEmpty()
309306
metric.SetName(buildMetricName(metricsNamespace, metricNameDuration))
310307
metric.SetUnit(p.config.Histogram.Unit.String())
311-
histograms.BuildMetrics(metric, startTimeGenerator, timestamp, p.config.GetAggregationTemporality())
308+
histograms.BuildMetrics(metric, timestamp, timeStampGenerator, p.config.GetAggregationTemporality())
312309
}
313310

314311
events := rawMetrics.events
315312
if p.events.Enabled {
316313
metric = sm.Metrics().AppendEmpty()
317314
metric.SetName(buildMetricName(metricsNamespace, metricNameEvents))
318-
events.BuildMetrics(metric, startTimeGenerator, timestamp, p.config.GetAggregationTemporality())
315+
events.BuildMetrics(metric, timestamp, timeStampGenerator, p.config.GetAggregationTemporality())
319316
}
320317

321318
for mk := range deltaMetricKeys {
@@ -379,7 +376,7 @@ func (p *connectorImp) aggregateMetrics(traces ptrace.Traces) {
379376
continue
380377
}
381378

382-
rm := p.getOrCreateResourceMetrics(resourceAttr, startTimestamp)
379+
rm := p.getOrCreateResourceMetrics(resourceAttr)
383380
sums := rm.sums
384381
histograms := rm.histograms
385382
events := rm.events
@@ -408,12 +405,12 @@ func (p *connectorImp) aggregateMetrics(traces ptrace.Traces) {
408405
}
409406
if !p.config.Histogram.Disable {
410407
// aggregate histogram metrics
411-
h := histograms.GetOrCreate(key, attributes)
408+
h := histograms.GetOrCreate(key, attributes, startTimestamp)
412409
p.addExemplar(span, duration, h)
413410
h.Observe(duration)
414411
}
415412
// aggregate sums metrics
416-
s := sums.GetOrCreate(key, attributes)
413+
s := sums.GetOrCreate(key, attributes, startTimestamp)
417414
if p.config.Exemplars.Enabled && !span.TraceID().IsEmpty() {
418415
s.AddExemplar(span.TraceID(), span.SpanID(), duration)
419416
}
@@ -437,7 +434,7 @@ func (p *connectorImp) aggregateMetrics(traces ptrace.Traces) {
437434
eAttributes = p.buildAttributes(serviceName, span, rscAndEventAttrs, eDimensions, ils.Scope())
438435
p.metricKeyToDimensions.Add(eKey, eAttributes)
439436
}
440-
e := events.GetOrCreate(eKey, eAttributes)
437+
e := events.GetOrCreate(eKey, eAttributes, startTimestamp)
441438
if p.config.Exemplars.Enabled && !span.TraceID().IsEmpty() {
442439
e.AddExemplar(span.TraceID(), span.SpanID(), duration)
443440
}
@@ -475,16 +472,15 @@ func (p *connectorImp) createResourceKey(attr pcommon.Map) resourceKey {
475472
return pdatautil.MapHash(m)
476473
}
477474

478-
func (p *connectorImp) getOrCreateResourceMetrics(attr pcommon.Map, startTimestamp pcommon.Timestamp) *resourceMetrics {
475+
func (p *connectorImp) getOrCreateResourceMetrics(attr pcommon.Map) *resourceMetrics {
479476
key := p.createResourceKey(attr)
480477
v, ok := p.resourceMetrics.Get(key)
481478
if !ok {
482479
v = &resourceMetrics{
483-
histograms: initHistogramMetrics(p.config),
484-
sums: metrics.NewSumMetrics(p.config.Exemplars.MaxPerDataPoint),
485-
events: metrics.NewSumMetrics(p.config.Exemplars.MaxPerDataPoint),
486-
attributes: attr,
487-
startTimestamp: startTimestamp,
480+
histograms: initHistogramMetrics(p.config),
481+
sums: metrics.NewSumMetrics(p.config.Exemplars.MaxPerDataPoint),
482+
events: metrics.NewSumMetrics(p.config.Exemplars.MaxPerDataPoint),
483+
attributes: attr,
488484
}
489485
p.resourceMetrics.Add(key, v)
490486
}

connector/spanmetricsconnector/internal/metrics/metrics.go

Lines changed: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ import (
1414
type Key string
1515

1616
type HistogramMetrics interface {
17-
GetOrCreate(key Key, attributes pcommon.Map) Histogram
18-
BuildMetrics(pmetric.Metric, generateStartTimestamp, pcommon.Timestamp, pmetric.AggregationTemporality)
17+
GetOrCreate(key Key, attributes pcommon.Map, startTimestamp pcommon.Timestamp) Histogram
18+
BuildMetrics(pmetric.Metric, pcommon.Timestamp, func(Key, pcommon.Timestamp) pcommon.Timestamp, pmetric.AggregationTemporality)
1919
ClearExemplars()
2020
}
2121

@@ -47,6 +47,8 @@ type explicitHistogram struct {
4747
bounds []float64
4848

4949
maxExemplarCount *int
50+
51+
startTimestamp pcommon.Timestamp
5052
}
5153

5254
type exponentialHistogram struct {
@@ -56,9 +58,9 @@ type exponentialHistogram struct {
5658
histogram *structure.Histogram[float64]
5759

5860
maxExemplarCount *int
59-
}
6061

61-
type generateStartTimestamp = func(Key) pcommon.Timestamp
62+
startTimestamp pcommon.Timestamp
63+
}
6264

6365
func NewExponentialHistogramMetrics(maxSize int32, maxExemplarCount *int) HistogramMetrics {
6466
return &exponentialHistogramMetrics{
@@ -76,7 +78,7 @@ func NewExplicitHistogramMetrics(bounds []float64, maxExemplarCount *int) Histog
7678
}
7779
}
7880

79-
func (m *explicitHistogramMetrics) GetOrCreate(key Key, attributes pcommon.Map) Histogram {
81+
func (m *explicitHistogramMetrics) GetOrCreate(key Key, attributes pcommon.Map, startTimestamp pcommon.Timestamp) Histogram {
8082
h, ok := m.metrics[key]
8183
if !ok {
8284
h = &explicitHistogram{
@@ -85,25 +87,26 @@ func (m *explicitHistogramMetrics) GetOrCreate(key Key, attributes pcommon.Map)
8587
bounds: m.bounds,
8688
bucketCounts: make([]uint64, len(m.bounds)+1),
8789
maxExemplarCount: m.maxExemplarCount,
90+
startTimestamp: startTimestamp,
8891
}
8992
m.metrics[key] = h
9093
}
91-
9294
return h
9395
}
9496

9597
func (m *explicitHistogramMetrics) BuildMetrics(
9698
metric pmetric.Metric,
97-
startTimestamp generateStartTimestamp,
9899
timestamp pcommon.Timestamp,
100+
startTimeStampGenerator func(Key, pcommon.Timestamp) pcommon.Timestamp,
99101
temporality pmetric.AggregationTemporality,
100102
) {
101103
metric.SetEmptyHistogram().SetAggregationTemporality(temporality)
102104
dps := metric.Histogram().DataPoints()
103105
dps.EnsureCapacity(len(m.metrics))
104106
for k, h := range m.metrics {
105107
dp := dps.AppendEmpty()
106-
dp.SetStartTimestamp(startTimestamp(k))
108+
startTimestamp := startTimeStampGenerator(k, h.startTimestamp)
109+
dp.SetStartTimestamp(startTimestamp)
107110
dp.SetTimestamp(timestamp)
108111
dp.ExplicitBounds().FromRaw(h.bounds)
109112
dp.BucketCounts().FromRaw(h.bucketCounts)
@@ -123,7 +126,7 @@ func (m *explicitHistogramMetrics) ClearExemplars() {
123126
}
124127
}
125128

126-
func (m *exponentialHistogramMetrics) GetOrCreate(key Key, attributes pcommon.Map) Histogram {
129+
func (m *exponentialHistogramMetrics) GetOrCreate(key Key, attributes pcommon.Map, startTimeStamp pcommon.Timestamp) Histogram {
127130
h, ok := m.metrics[key]
128131
if !ok {
129132
histogram := new(structure.Histogram[float64])
@@ -137,32 +140,33 @@ func (m *exponentialHistogramMetrics) GetOrCreate(key Key, attributes pcommon.Ma
137140
attributes: attributes,
138141
exemplars: pmetric.NewExemplarSlice(),
139142
maxExemplarCount: m.maxExemplarCount,
143+
startTimestamp: startTimeStamp,
140144
}
141145
m.metrics[key] = h
142146
}
143-
144147
return h
145148
}
146149

147150
func (m *exponentialHistogramMetrics) BuildMetrics(
148151
metric pmetric.Metric,
149-
startTimestamp generateStartTimestamp,
150152
timestamp pcommon.Timestamp,
153+
startTimeStampGenerator func(Key, pcommon.Timestamp) pcommon.Timestamp,
151154
temporality pmetric.AggregationTemporality,
152155
) {
153156
metric.SetEmptyExponentialHistogram().SetAggregationTemporality(temporality)
154157
dps := metric.ExponentialHistogram().DataPoints()
155158
dps.EnsureCapacity(len(m.metrics))
156-
for k, m := range m.metrics {
159+
for k, e := range m.metrics {
157160
dp := dps.AppendEmpty()
158-
dp.SetStartTimestamp(startTimestamp(k))
161+
startTimestamp := startTimeStampGenerator(k, e.startTimestamp)
162+
dp.SetStartTimestamp(startTimestamp)
159163
dp.SetTimestamp(timestamp)
160-
expoHistToExponentialDataPoint(m.histogram, dp)
161-
for i := 0; i < m.exemplars.Len(); i++ {
162-
m.exemplars.At(i).SetTimestamp(timestamp)
164+
expoHistToExponentialDataPoint(e.histogram, dp)
165+
for i := 0; i < e.exemplars.Len(); i++ {
166+
e.exemplars.At(i).SetTimestamp(timestamp)
163167
}
164-
m.exemplars.CopyTo(dp.Exemplars())
165-
m.attributes.CopyTo(dp.Attributes())
168+
e.exemplars.CopyTo(dp.Exemplars())
169+
e.attributes.CopyTo(dp.Attributes())
166170
}
167171
}
168172

@@ -237,10 +241,13 @@ func (h *exponentialHistogram) AddExemplar(traceID pcommon.TraceID, spanID pcomm
237241
}
238242

239243
type Sum struct {
240-
attributes pcommon.Map
241-
count uint64
244+
attributes pcommon.Map
245+
count uint64
246+
242247
exemplars pmetric.ExemplarSlice
243248
maxExemplarCount *int
249+
250+
startTimestamp pcommon.Timestamp
244251
// isFirst is used to track if this datapoint is new to the Sum. This
245252
// is used to ensure that new Sum metrics being with 0, and then are incremented
246253
// to the desired value. This avoids Prometheus throwing away the first
@@ -264,13 +271,14 @@ type SumMetrics struct {
264271
maxExemplarCount *int
265272
}
266273

267-
func (m *SumMetrics) GetOrCreate(key Key, attributes pcommon.Map) *Sum {
274+
func (m *SumMetrics) GetOrCreate(key Key, attributes pcommon.Map, startTimestamp pcommon.Timestamp) *Sum {
268275
s, ok := m.metrics[key]
269276
if !ok {
270277
s = &Sum{
271278
attributes: attributes,
272279
exemplars: pmetric.NewExemplarSlice(),
273280
maxExemplarCount: m.maxExemplarCount,
281+
startTimestamp: startTimestamp,
274282
isFirst: true,
275283
}
276284
m.metrics[key] = s
@@ -290,8 +298,8 @@ func (s *Sum) AddExemplar(traceID pcommon.TraceID, spanID pcommon.SpanID, value
290298

291299
func (m *SumMetrics) BuildMetrics(
292300
metric pmetric.Metric,
293-
startTimestamp generateStartTimestamp,
294301
timestamp pcommon.Timestamp,
302+
startTimeStampGenerator func(Key, pcommon.Timestamp) pcommon.Timestamp,
295303
temporality pmetric.AggregationTemporality,
296304
) {
297305
metric.SetEmptySum().SetIsMonotonic(true)
@@ -301,7 +309,8 @@ func (m *SumMetrics) BuildMetrics(
301309
dps.EnsureCapacity(len(m.metrics))
302310
for k, s := range m.metrics {
303311
dp := dps.AppendEmpty()
304-
dp.SetStartTimestamp(startTimestamp(k))
312+
startTimeStamp := startTimeStampGenerator(k, s.startTimestamp)
313+
dp.SetStartTimestamp(startTimeStamp)
305314
dp.SetTimestamp(timestamp)
306315
if s.isFirst {
307316
dp.SetIntValue(0)

0 commit comments

Comments
 (0)