Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions prometheus_client/metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@ def _is_observable(self):
# * the child of a labelled metric.
return not self._labelnames or (self._labelnames and self._labelvalues)

def _raise_if_not_observable(self):
# Functions that mutate the state of the metric, for example incrementing
# a counter, will fail if the metric is not observable, because only if a
# metric is observable will the value be initialized.
if not self._is_observable():
raise ValueError('%s metric is missing label values' % str(self._type))

def _is_parent(self):
return self._labelnames and not self._labelvalues

Expand Down Expand Up @@ -250,6 +257,7 @@ def count_exceptions(self, exception=Exception):
Increments the counter when an exception of the given
type is raised up out of the code.
"""
self._raise_if_not_observable()
return ExceptionCounter(self, exception)

def _child_samples(self):
Expand Down Expand Up @@ -354,13 +362,15 @@ def track_inprogress(self):
Increments the gauge when the code is entered,
and decrements when it is exited.
"""
self._raise_if_not_observable()
return InprogressTracker(self)

def time(self):
"""Time a block of code or function, and set the duration in seconds.

Can be used as a function decorator or context manager.
"""
self._raise_if_not_observable()
return Timer(self.set)

def set_function(self, f):
Expand Down Expand Up @@ -428,6 +438,7 @@ def time(self):

Can be used as a function decorator or context manager.
"""
self._raise_if_not_observable()
return Timer(self.observe)

def _child_samples(self):
Expand Down
32 changes: 32 additions & 0 deletions tests/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,14 @@ def test_block_decorator(self):
self.assertTrue(raised)
self.assertEqual(1, self.registry.get_sample_value('c_total'))

def test_count_exceptions_not_observable(self):
counter = Counter('counter', 'help', labelnames=('label',), registry=self.registry)

try:
counter.count_exceptions()
except ValueError as e:
self.assertIn('missing label values', str(e))


class TestGauge(unittest.TestCase):
def setUp(self):
Expand Down Expand Up @@ -150,6 +158,22 @@ def test_time_block_decorator(self):
time.sleep(.001)
self.assertNotEqual(0, self.registry.get_sample_value('g'))

def test_track_in_progress_not_observable(self):
g = Gauge('test', 'help', labelnames=('label',), registry=self.registry)

try:
g.track_inprogress()
except ValueError as e:
self.assertIn('missing label values', str(e))

def test_timer_not_observable(self):
g = Gauge('test', 'help', labelnames=('label',), registry=self.registry)

try:
g.time()
except ValueError as e:
self.assertIn('missing label values', str(e))


class TestSummary(unittest.TestCase):
def setUp(self):
Expand Down Expand Up @@ -230,6 +254,14 @@ def test_block_decorator(self):
pass
self.assertEqual(1, self.registry.get_sample_value('s_count'))

def test_timer_not_observable(self):
s = Summary('test', 'help', labelnames=('label',), registry=self.registry)

try:
s.time()
except ValueError as e:
self.assertIn('missing label values', str(e))


class TestHistogram(unittest.TestCase):
def setUp(self):
Expand Down