Skip to content

Commit a560cb6

Browse files
committed
Count NaN histogram observations in the overflow bucket
Signed-off-by: Zhe Chen <zhechen.cs@connect.hku.hk>
1 parent ff9a2d3 commit a560cb6

3 files changed

Lines changed: 30 additions & 0 deletions

File tree

‎prometheus_client/metrics.py‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,8 @@ def observe(self, amount: float, exemplar: Optional[Dict[str, str]] = None) -> N
680680
observations. See
681681
https://prometheus.io/docs/practices/histograms/#count-and-sum-of-observations
682682
for details.
683+
684+
NaN observations are counted in the +Inf bucket and make the sum NaN.
683685
"""
684686
self._raise_if_not_observable()
685687
self._sum.inc(amount)
@@ -690,6 +692,8 @@ def observe(self, amount: float, exemplar: Optional[Dict[str, str]] = None) -> N
690692
_validate_exemplar(exemplar)
691693
self._buckets[i].set_exemplar(Exemplar(exemplar, amount, time.time()))
692694
break
695+
else:
696+
self._buckets[-1].inc(1)
693697

694698
def time(self) -> Timer:
695699
"""Time a block of code or function, and observe the duration in seconds.

‎tests/test_core.py‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from concurrent.futures import ThreadPoolExecutor
2+
import math
23
import os
34
from threading import Barrier, Event
45
import time
@@ -445,6 +446,18 @@ def test_histogram(self):
445446
self.assertEqual(3, self.registry.get_sample_value('h_count'))
446447
self.assertEqual(float("inf"), self.registry.get_sample_value('h_sum'))
447448

449+
def test_histogram_nan(self):
450+
self.histogram.observe(2)
451+
self.histogram.observe(float('nan'))
452+
self.histogram.observe(3)
453+
454+
self.assertEqual(0, self.registry.get_sample_value('h_bucket', {'le': '1.0'}))
455+
self.assertEqual(1, self.registry.get_sample_value('h_bucket', {'le': '2.5'}))
456+
self.assertEqual(2, self.registry.get_sample_value('h_bucket', {'le': '5.0'}))
457+
self.assertEqual(3, self.registry.get_sample_value('h_bucket', {'le': '+Inf'}))
458+
self.assertEqual(3, self.registry.get_sample_value('h_count'))
459+
self.assertTrue(math.isnan(self.registry.get_sample_value('h_sum')))
460+
448461
def test_histogram_not_observable(self):
449462
""".observe() must fail if the Summary is not observable."""
450463
assert_not_observable(self.labels.observe, 1)

‎tests/test_multiprocess.py‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import glob
2+
import math
23
import os
34
import shutil
45
import tempfile
@@ -109,6 +110,18 @@ def test_histogram_adds(self):
109110
self.assertEqual(3, self.registry.get_sample_value('h_sum'))
110111
self.assertEqual(2, self.registry.get_sample_value('h_bucket', {'le': '5.0'}))
111112

113+
def test_histogram_nan(self):
114+
h1 = Histogram('h', 'help', registry=None)
115+
values.ValueClass = MultiProcessValue(lambda: 456)
116+
h2 = Histogram('h', 'help', registry=None)
117+
h1.observe(float('nan'))
118+
h2.observe(2)
119+
120+
self.assertEqual(1, self.registry.get_sample_value('h_bucket', {'le': '5.0'}))
121+
self.assertEqual(2, self.registry.get_sample_value('h_bucket', {'le': '+Inf'}))
122+
self.assertEqual(2, self.registry.get_sample_value('h_count'))
123+
self.assertTrue(math.isnan(self.registry.get_sample_value('h_sum')))
124+
112125
def test_gauge_all(self):
113126
g1 = Gauge('g', 'help', registry=None)
114127
values.ValueClass = MultiProcessValue(lambda: 456)

0 commit comments

Comments
 (0)