@@ -41,12 +41,23 @@ type resourceKey struct {
41
41
job string
42
42
instance string
43
43
}
44
+
45
+ // The name of the metric family doesn't include magic suffixes (e.g. _bucket),
46
+ // so for a classic histgram and a native histogram of the same family, the
47
+ // metric family will be the same. To be able to tell them apart, we need to
48
+ // store whether the metric is a native histogram or not.
49
+ type metricFamilyKey struct {
50
+ isExponentialHistogram bool
51
+ name string
52
+ }
53
+
44
54
type transaction struct {
45
55
isNew bool
46
56
trimSuffixes bool
47
57
enableNativeHistograms bool
58
+ addingNativeHistogram bool // true if the last sample was a native histogram.
48
59
ctx context.Context
49
- families map [resourceKey ]map [scopeID ]map [string ]* metricFamily
60
+ families map [resourceKey ]map [scopeID ]map [metricFamilyKey ]* metricFamily
50
61
mc scrape.MetricMetadataStore
51
62
sink consumer.Metrics
52
63
externalLabels labels.Labels
@@ -79,7 +90,7 @@ func newTransaction(
79
90
) * transaction {
80
91
return & transaction {
81
92
ctx : ctx ,
82
- families : make (map [resourceKey ]map [scopeID ]map [string ]* metricFamily ),
93
+ families : make (map [resourceKey ]map [scopeID ]map [metricFamilyKey ]* metricFamily ),
83
94
isNew : true ,
84
95
trimSuffixes : trimSuffixes ,
85
96
enableNativeHistograms : enableNativeHistograms ,
@@ -97,6 +108,8 @@ func newTransaction(
97
108
98
109
// Append always returns 0 to disable label caching.
99
110
func (t * transaction ) Append (_ storage.SeriesRef , ls labels.Labels , atMs int64 , val float64 ) (storage.SeriesRef , error ) {
111
+ t .addingNativeHistogram = false
112
+
100
113
select {
101
114
case <- t .ctx .Done ():
102
115
return 0 , errTransactionAborted
@@ -157,64 +170,88 @@ func (t *transaction) Append(_ storage.SeriesRef, ls labels.Labels, atMs int64,
157
170
return 0 , nil
158
171
}
159
172
160
- curMF , existing := t . getOrCreateMetricFamily ( * rKey , getScopeID (ls ), metricName )
173
+ scope := getScopeID (ls )
161
174
162
- if t .enableNativeHistograms && curMF .mtype == pmetric .MetricTypeExponentialHistogram {
163
- // If a histogram has both classic and native version, the native histogram is scraped
164
- // first. Getting a float sample for the same series means that `scrape_classic_histogram`
165
- // is set to true in the scrape config. In this case, we should ignore the native histogram.
166
- curMF .mtype = pmetric .MetricTypeHistogram
175
+ if t .enableNativeHistograms && value .IsStaleNaN (val ) {
176
+ if t .detectAndStoreNativeHistogramStaleness (atMs , rKey , scope , metricName , ls ) {
177
+ return 0 , nil
178
+ }
167
179
}
168
180
181
+ curMF := t .getOrCreateMetricFamily (* rKey , scope , metricName )
182
+
169
183
seriesRef := t .getSeriesRef (ls , curMF .mtype )
170
184
err = curMF .addSeries (seriesRef , metricName , ls , atMs , val )
171
185
if err != nil {
172
- // Handle special case of float sample indicating staleness of native
173
- // histogram. This is similar to how Prometheus handles it, but we
174
- // don't have access to the previous value so we're applying some
175
- // heuristics to figure out if this is native histogram or not.
176
- // The metric type will indicate histogram, but presumably there will be no
177
- // _bucket, _count, _sum suffix or `le` label, which makes addSeries fail
178
- // with errEmptyLeLabel.
179
- if t .enableNativeHistograms && errors .Is (err , errEmptyLeLabel ) && ! existing && value .IsStaleNaN (val ) && curMF .mtype == pmetric .MetricTypeHistogram {
180
- mg := curMF .loadMetricGroupOrCreate (seriesRef , ls , atMs )
181
- curMF .mtype = pmetric .MetricTypeExponentialHistogram
182
- mg .mtype = pmetric .MetricTypeExponentialHistogram
183
- _ = curMF .addExponentialHistogramSeries (seriesRef , metricName , ls , atMs , & histogram.Histogram {Sum : math .Float64frombits (value .StaleNaN )}, nil )
184
- // ignore errors here, this is best effort.
185
- } else {
186
- t .logger .Warn ("failed to add datapoint" , zap .Error (err ), zap .String ("metric_name" , metricName ), zap .Any ("labels" , ls ))
187
- }
186
+ t .logger .Warn ("failed to add datapoint" , zap .Error (err ), zap .String ("metric_name" , metricName ), zap .Any ("labels" , ls ))
188
187
}
189
188
190
189
return 0 , nil // never return errors, as that fails the whole scrape
191
190
}
192
191
192
+ // detectAndStoreNativeHistogramStaleness returns true if it detects
193
+ // and stores a native histogram staleness marker.
194
+ func (t * transaction ) detectAndStoreNativeHistogramStaleness (atMs int64 , key * resourceKey , scope scopeID , metricName string , ls labels.Labels ) bool {
195
+ // Detect the special case of stale native histogram series.
196
+ // Currently Prometheus does not store the histogram type in
197
+ // its staleness tracker.
198
+ md , ok := t .mc .GetMetadata (metricName )
199
+ if ! ok {
200
+ // Native histograms always have metadata.
201
+ return false
202
+ }
203
+ if md .Type != model .MetricTypeHistogram {
204
+ // Not a histogram.
205
+ return false
206
+ }
207
+ if md .Metric != metricName {
208
+ // Not a native histogram because it has magic suffixes (e.g. _bucket).
209
+ return false
210
+ }
211
+ // Store the staleness marker as a native histogram.
212
+ t .addingNativeHistogram = true
213
+
214
+ curMF := t .getOrCreateMetricFamily (* key , scope , metricName )
215
+ seriesRef := t .getSeriesRef (ls , curMF .mtype )
216
+
217
+ _ = curMF .addExponentialHistogramSeries (seriesRef , metricName , ls , atMs , & histogram.Histogram {Sum : math .Float64frombits (value .StaleNaN )}, nil )
218
+ // ignore errors here, this is best effort.
219
+
220
+ return true
221
+ }
222
+
193
223
// getOrCreateMetricFamily returns the metric family for the given metric name and scope,
194
224
// and true if an existing family was found.
195
- func (t * transaction ) getOrCreateMetricFamily (key resourceKey , scope scopeID , mn string ) ( * metricFamily , bool ) {
225
+ func (t * transaction ) getOrCreateMetricFamily (key resourceKey , scope scopeID , mn string ) * metricFamily {
196
226
if _ , ok := t .families [key ]; ! ok {
197
- t .families [key ] = make (map [scopeID ]map [string ]* metricFamily )
227
+ t .families [key ] = make (map [scopeID ]map [metricFamilyKey ]* metricFamily )
198
228
}
199
229
if _ , ok := t.families [key ][scope ]; ! ok {
200
- t.families [key ][scope ] = make (map [string ]* metricFamily )
230
+ t.families [key ][scope ] = make (map [metricFamilyKey ]* metricFamily )
201
231
}
202
232
203
- curMf , ok := t.families [key ][scope ][mn ]
233
+ mfKey := metricFamilyKey {isExponentialHistogram : t .addingNativeHistogram , name : mn }
234
+
235
+ curMf , ok := t.families [key ][scope ][mfKey ]
236
+
204
237
if ! ok {
205
238
fn := mn
206
239
if _ , ok := t .mc .GetMetadata (mn ); ! ok {
207
240
fn = normalizeMetricName (mn )
208
241
}
209
- mf , ok := t.families [key ][scope ][fn ]
242
+ fnKey := metricFamilyKey {isExponentialHistogram : mfKey .isExponentialHistogram , name : fn }
243
+ mf , ok := t.families [key ][scope ][fnKey ]
210
244
if ! ok || ! mf .includesMetric (mn ) {
211
245
curMf = newMetricFamily (mn , t .mc , t .logger )
212
- t.families [key ][scope ][curMf .name ] = curMf
213
- return curMf , false
246
+ if curMf .mtype == pmetric .MetricTypeHistogram && mfKey .isExponentialHistogram {
247
+ curMf .mtype = pmetric .MetricTypeExponentialHistogram
248
+ }
249
+ t.families [key ][scope ][metricFamilyKey {isExponentialHistogram : mfKey .isExponentialHistogram , name : curMf .name }] = curMf
250
+ return curMf
214
251
}
215
252
curMf = mf
216
253
}
217
- return curMf , true
254
+ return curMf
218
255
}
219
256
220
257
func (t * transaction ) AppendExemplar (_ storage.SeriesRef , l labels.Labels , e exemplar.Exemplar ) (storage.SeriesRef , error ) {
@@ -240,7 +277,13 @@ func (t *transaction) AppendExemplar(_ storage.SeriesRef, l labels.Labels, e exe
240
277
return 0 , errMetricNameNotFound
241
278
}
242
279
243
- mf , _ := t .getOrCreateMetricFamily (* rKey , getScopeID (l ), mn )
280
+ mf := t .getOrCreateMetricFamily (* rKey , getScopeID (l ), mn )
281
+
282
+ // Workaround for https://github.com/prometheus/prometheus/issues/16217
283
+ if ! t .enableNativeHistograms && mf .mtype == pmetric .MetricTypeHistogram && l .Get (model .MetricNameLabel ) == mf .name {
284
+ return 0 , nil
285
+ }
286
+
244
287
mf .addExemplar (t .getSeriesRef (l , mf .mtype ), e )
245
288
246
289
return 0 , nil
@@ -257,6 +300,8 @@ func (t *transaction) AppendHistogram(_ storage.SeriesRef, ls labels.Labels, atM
257
300
default :
258
301
}
259
302
303
+ t .addingNativeHistogram = true
304
+
260
305
if t .externalLabels .Len () != 0 {
261
306
b := labels .NewBuilder (ls )
262
307
t .externalLabels .Range (func (l labels.Label ) {
@@ -286,13 +331,7 @@ func (t *transaction) AppendHistogram(_ storage.SeriesRef, ls labels.Labels, atM
286
331
// The `up`, `target_info`, `otel_scope_info` metrics should never generate native histograms,
287
332
// thus we don't check for them here as opposed to the Append function.
288
333
289
- curMF , existing := t .getOrCreateMetricFamily (* rKey , getScopeID (ls ), metricName )
290
- if ! existing {
291
- curMF .mtype = pmetric .MetricTypeExponentialHistogram
292
- } else if curMF .mtype != pmetric .MetricTypeExponentialHistogram {
293
- // Already scraped as classic histogram.
294
- return 0 , nil
295
- }
334
+ curMF := t .getOrCreateMetricFamily (* rKey , getScopeID (ls ), metricName )
296
335
297
336
if h != nil && h .CounterResetHint == histogram .GaugeType || fh != nil && fh .CounterResetHint == histogram .GaugeType {
298
337
t .logger .Warn ("dropping unsupported gauge histogram datapoint" , zap .String ("metric_name" , metricName ), zap .Any ("labels" , ls ))
@@ -307,14 +346,16 @@ func (t *transaction) AppendHistogram(_ storage.SeriesRef, ls labels.Labels, atM
307
346
}
308
347
309
348
func (t * transaction ) AppendCTZeroSample (_ storage.SeriesRef , ls labels.Labels , atMs , ctMs int64 ) (storage.SeriesRef , error ) {
310
- return t .setCreationTimestamp (ls , atMs , ctMs , false )
349
+ t .addingNativeHistogram = false
350
+ return t .setCreationTimestamp (ls , atMs , ctMs )
311
351
}
312
352
313
353
func (t * transaction ) AppendHistogramCTZeroSample (_ storage.SeriesRef , ls labels.Labels , atMs , ctMs int64 , _ * histogram.Histogram , _ * histogram.FloatHistogram ) (storage.SeriesRef , error ) {
314
- return t .setCreationTimestamp (ls , atMs , ctMs , true )
354
+ t .addingNativeHistogram = true
355
+ return t .setCreationTimestamp (ls , atMs , ctMs )
315
356
}
316
357
317
- func (t * transaction ) setCreationTimestamp (ls labels.Labels , atMs , ctMs int64 , histogram bool ) (storage.SeriesRef , error ) {
358
+ func (t * transaction ) setCreationTimestamp (ls labels.Labels , atMs , ctMs int64 ) (storage.SeriesRef , error ) {
318
359
select {
319
360
case <- t .ctx .Done ():
320
361
return 0 , errTransactionAborted
@@ -347,16 +388,7 @@ func (t *transaction) setCreationTimestamp(ls labels.Labels, atMs, ctMs int64, h
347
388
return 0 , errMetricNameNotFound
348
389
}
349
390
350
- curMF , existing := t .getOrCreateMetricFamily (* rKey , getScopeID (ls ), metricName )
351
-
352
- if histogram {
353
- if ! existing {
354
- curMF .mtype = pmetric .MetricTypeExponentialHistogram
355
- } else if curMF .mtype != pmetric .MetricTypeExponentialHistogram {
356
- // Already scraped as classic histogram.
357
- return 0 , nil
358
- }
359
- }
391
+ curMF := t .getOrCreateMetricFamily (* rKey , getScopeID (ls ), metricName )
360
392
361
393
seriesRef := t .getSeriesRef (ls , curMF .mtype )
362
394
curMF .addCreationTimestamp (seriesRef , ls , atMs , ctMs )
@@ -543,6 +575,7 @@ func (t *transaction) UpdateMetadata(_ storage.SeriesRef, _ labels.Labels, _ met
543
575
}
544
576
545
577
func (t * transaction ) AddTargetInfo (key resourceKey , ls labels.Labels ) {
578
+ t .addingNativeHistogram = false
546
579
if resource , ok := t .nodeResources [key ]; ok {
547
580
attrs := resource .Attributes ()
548
581
ls .Range (func (lbl labels.Label ) {
@@ -555,6 +588,7 @@ func (t *transaction) AddTargetInfo(key resourceKey, ls labels.Labels) {
555
588
}
556
589
557
590
func (t * transaction ) addScopeInfo (key resourceKey , ls labels.Labels ) {
591
+ t .addingNativeHistogram = false
558
592
attrs := pcommon .NewMap ()
559
593
scope := scopeID {}
560
594
ls .Range (func (lbl labels.Label ) {
0 commit comments