From f60e5058f7b02048c3d49c64af4538f648924498 Mon Sep 17 00:00:00 2001 From: Maykel Moya Date: Wed, 18 Mar 2015 10:26:44 +0100 Subject: [PATCH] Change indentation from 2 to 4 spaces --- prometheus_client/__init__.py | 710 +++++++++++++++++----------------- tests/test_client.py | 464 +++++++++++----------- 2 files changed, 594 insertions(+), 580 deletions(-) diff --git a/prometheus_client/__init__.py b/prometheus_client/__init__.py index 835e5e08..2e2bea49 100644 --- a/prometheus_client/__init__.py +++ b/prometheus_client/__init__.py @@ -8,10 +8,10 @@ import time import threading try: - from BaseHTTPServer import BaseHTTPRequestHandler + from BaseHTTPServer import BaseHTTPRequestHandler except ImportError: - # Python 3 - from http.server import BaseHTTPRequestHandler + # Python 3 + from http.server import BaseHTTPRequestHandler from functools import wraps from threading import Lock @@ -25,47 +25,47 @@ class CollectorRegistry(object): - '''Metric collector registry. - - Collectors must have a no-argument method 'collect' that returns a list of - Metric objects. The returned metrics should be consistent with the Prometheus - exposition formats. - ''' - def __init__(self): - self._collectors = set() - self._lock = Lock() - - def register(self, collector): - '''Add a collector to the registry.''' - with self._lock: - self._collectors.add(collector) - - def unregister(self, collector): - '''Remove a collector from the registry.''' - with self._lock: - self._collectors.remove(collector) - - def collect(self): - '''Yields metrics from the collectors in the registry.''' - collectors = None - with self._lock: - collectors = copy.copy(self._collectors) - for collector in collectors: - for metric in collector.collect(): - yield metric - - def get_sample_value(self, name, labels=None): - '''Returns the sample value, or None if not found. - - This is inefficient, and intended only for use in unittests. + '''Metric collector registry. + + Collectors must have a no-argument method 'collect' that returns a list of + Metric objects. The returned metrics should be consistent with the Prometheus + exposition formats. ''' - if labels is None: - labels = {} - for metric in self.collect(): - for n, l, value in metric._samples: - if n == name and l == labels: - return value - return None + def __init__(self): + self._collectors = set() + self._lock = Lock() + + def register(self, collector): + '''Add a collector to the registry.''' + with self._lock: + self._collectors.add(collector) + + def unregister(self, collector): + '''Remove a collector from the registry.''' + with self._lock: + self._collectors.remove(collector) + + def collect(self): + '''Yields metrics from the collectors in the registry.''' + collectors = None + with self._lock: + collectors = copy.copy(self._collectors) + for collector in collectors: + for metric in collector.collect(): + yield metric + + def get_sample_value(self, name, labels=None): + '''Returns the sample value, or None if not found. + + This is inefficient, and intended only for use in unittests. + ''' + if labels is None: + labels = {} + for metric in self.collect(): + for n, l, value in metric._samples: + if n == name and l == labels: + return value + return None REGISTRY = CollectorRegistry() @@ -75,324 +75,332 @@ def get_sample_value(self, name, labels=None): class Metric(object): - '''A single metric and it's samples.''' - def __init__(self, name, documentation, typ): - self._name = name - self._documentation = documentation - if typ not in _METRIC_TYPES: - raise ValueError('Invalid metric type: ' + typ) - self._type = typ - self._samples = [] + '''A single metric and it's samples.''' + def __init__(self, name, documentation, typ): + self._name = name + self._documentation = documentation + if typ not in _METRIC_TYPES: + raise ValueError('Invalid metric type: ' + typ) + self._type = typ + self._samples = [] - '''Add a sample to the metric''' - def add_sample(self, name, labels, value): - self._samples.append((name, labels, value)) + '''Add a sample to the metric''' + def add_sample(self, name, labels, value): + self._samples.append((name, labels, value)) class _LabelWrapper(object): - '''Handles labels for the wrapped metric.''' - def __init__(self, wrappedClass, labelnames, **kwargs): - self._wrappedClass = wrappedClass - self._type = wrappedClass._type - self._labelnames = labelnames - self._kwargs = kwargs - self._lock = Lock() - self._metrics = {} - - for l in labelnames: - if l.startswith('__'): - raise ValueError('Invalid label metric name: ' + l) - - def labels(self, *labelvalues): - '''Return the child for the given labelset.''' - if len(labelvalues) != len(self._labelnames): - raise ValueError('Incorrect label count') - labelvalues = tuple(labelvalues) - with self._lock: - if labelvalues not in self._metrics: - self._metrics[labelvalues] = self._wrappedClass(**self._kwargs) - return self._metrics[labelvalues] - - def remove(self, *labelvalues): - '''Remove the given labelset from the metric.''' - if len(labelvalues) != len(self._labelnames): - raise ValueError('Incorrect label count') - labelvalues = tuple(labelvalues) - with self._lock: - del self._metrics[labelvalues] - - def _samples(self): - with self._lock: - metrics = self._metrics.copy() - for labels, metric in metrics.items(): - series_labels = list(dict(zip(self._labelnames, labels)).items()) - for suffix, sample_labels, value in metric._samples(): - yield (suffix, dict(series_labels + list(sample_labels.items())), value) + '''Handles labels for the wrapped metric.''' + def __init__(self, wrappedClass, labelnames, **kwargs): + self._wrappedClass = wrappedClass + self._type = wrappedClass._type + self._labelnames = labelnames + self._kwargs = kwargs + self._lock = Lock() + self._metrics = {} + + for l in labelnames: + if l.startswith('__'): + raise ValueError('Invalid label metric name: ' + l) + + def labels(self, *labelvalues): + '''Return the child for the given labelset.''' + if len(labelvalues) != len(self._labelnames): + raise ValueError('Incorrect label count') + labelvalues = tuple(labelvalues) + with self._lock: + if labelvalues not in self._metrics: + self._metrics[labelvalues] = self._wrappedClass(**self._kwargs) + return self._metrics[labelvalues] + + def remove(self, *labelvalues): + '''Remove the given labelset from the metric.''' + if len(labelvalues) != len(self._labelnames): + raise ValueError('Incorrect label count') + labelvalues = tuple(labelvalues) + with self._lock: + del self._metrics[labelvalues] + + def _samples(self): + with self._lock: + metrics = self._metrics.copy() + for labels, metric in metrics.items(): + series_labels = list(dict(zip(self._labelnames, labels)).items()) + for suffix, sample_labels, value in metric._samples(): + yield (suffix, dict(series_labels + list(sample_labels.items())), value) def _MetricWrapper(cls): - '''Provides common functionality for metrics.''' - def init(name, documentation, labelnames=(), namespace='', subsystem='', registry=REGISTRY, **kwargs): - if labelnames: - for l in labelnames: - if not _METRIC_LABEL_NAME_RE.match(l): - raise ValueError('Invalid label metric name: ' + l) - if _RESERVED_METRIC_LABEL_NAME_RE.match(l): - raise ValueError('Reserved label metric name: ' + l) - if l in cls._reserved_labelnames: - raise ValueError('Reserved label metric name: ' + l) - collector = _LabelWrapper(cls, labelnames, **kwargs) - else: - collector = cls(**kwargs) + '''Provides common functionality for metrics.''' + def init(name, documentation, labelnames=(), namespace='', subsystem='', registry=REGISTRY, **kwargs): + if labelnames: + for l in labelnames: + if not _METRIC_LABEL_NAME_RE.match(l): + raise ValueError('Invalid label metric name: ' + l) + if _RESERVED_METRIC_LABEL_NAME_RE.match(l): + raise ValueError('Reserved label metric name: ' + l) + if l in cls._reserved_labelnames: + raise ValueError('Reserved label metric name: ' + l) + collector = _LabelWrapper(cls, labelnames, **kwargs) + else: + collector = cls(**kwargs) - full_name = '' - if namespace: - full_name += namespace + '_' - if subsystem: - full_name += subsystem + '_' - full_name += name + full_name = '' + if namespace: + full_name += namespace + '_' + if subsystem: + full_name += subsystem + '_' + full_name += name - if not _METRIC_NAME_RE.match(full_name): - raise ValueError('Invalid metric name: ' + full_name) + if not _METRIC_NAME_RE.match(full_name): + raise ValueError('Invalid metric name: ' + full_name) - def collect(): - metric = Metric(full_name, documentation, cls._type) - for suffix, labels, value in collector._samples(): - metric.add_sample(full_name + suffix, labels, value) - return [metric] - collector.collect = collect + def collect(): + metric = Metric(full_name, documentation, cls._type) + for suffix, labels, value in collector._samples(): + metric.add_sample(full_name + suffix, labels, value) + return [metric] + collector.collect = collect - if registry: - registry.register(collector) - return collector + if registry: + registry.register(collector) + return collector - return init + return init @_MetricWrapper class Counter(object): - _type = 'counter' - _reserved_labelnames = [] - - def __init__(self): - self._value = 0.0 - self._lock = Lock() - - def inc(self, amount=1): - '''Increment counter by the given amount.''' - if amount < 0: - raise ValueError('Counters can only be incremented by non-negative amounts.') - with self._lock: - self._value += amount - - def count_exceptions(self, exception=Exception): - '''Count exceptions in a block of code or function. - - Can be used as a function decorator or context manager. - Increments the counter when an exception of the given - type is raised up out of the code. - ''' - class ExceptionCounter(object): - def __init__(self, counter): - self._counter = counter + _type = 'counter' + _reserved_labelnames = [] + + def __init__(self): + self._value = 0.0 + self._lock = Lock() + + def inc(self, amount=1): + '''Increment counter by the given amount.''' + if amount < 0: + raise ValueError('Counters can only be incremented by non-negative amounts.') + with self._lock: + self._value += amount + + def count_exceptions(self, exception=Exception): + '''Count exceptions in a block of code or function. - def __enter__(self): - pass + Can be used as a function decorator or context manager. + Increments the counter when an exception of the given + type is raised up out of the code. + ''' - def __exit__(self, typ, value, traceback): - if isinstance(value, exception): - self._counter.inc() + class ExceptionCounter(object): + def __init__(self, counter): + self._counter = counter - def __call__(self, f): - @wraps(f) - def wrapped(*args, **kwargs): - with self: - return f(*args, **kwargs) - return wrapped - return ExceptionCounter(self) + def __enter__(self): + pass - def _samples(self): - with self._lock: - return (('', {}, self._value), ) + def __exit__(self, typ, value, traceback): + if isinstance(value, exception): + self._counter.inc() + + def __call__(self, f): + @wraps(f) + def wrapped(*args, **kwargs): + with self: + return f(*args, **kwargs) + return wrapped + + return ExceptionCounter(self) + + def _samples(self): + with self._lock: + return (('', {}, self._value), ) @_MetricWrapper class Gauge(object): - _type = 'gauge' - _reserved_labelnames = [] - - def __init__(self): - self._value = 0.0 - self._lock = Lock() - - def inc(self, amount=1): - '''Increment gauge by the given amount.''' - with self._lock: - self._value += amount - - def dec(self, amount=1): - '''Decrement gauge by the given amount.''' - with self._lock: - self._value -= amount - - def set(self, value): - '''Set gauge to the given value.''' - with self._lock: - self._value = float(value) - - def set_to_current_time(self): - '''Set gauge to the current unixtime.''' - self.set(time.time()) - - def track_inprogress(self): - '''Track inprogress blocks of code or functions. - - Can be used as a function decorator or context manager. - Increments the gauge when the code is entered, - and decrements when it is exited. - ''' - class InprogressTracker(object): - def __init__(self, gauge): - self._gauge = gauge + _type = 'gauge' + _reserved_labelnames = [] + + def __init__(self): + self._value = 0.0 + self._lock = Lock() + + def inc(self, amount=1): + '''Increment gauge by the given amount.''' + with self._lock: + self._value += amount - def __enter__(self): - self._gauge.inc() + def dec(self, amount=1): + '''Decrement gauge by the given amount.''' + with self._lock: + self._value -= amount - def __exit__(self, typ, value, traceback): - self._gauge.dec() + def set(self, value): + '''Set gauge to the given value.''' + with self._lock: + self._value = float(value) - def __call__(self, f): - @wraps(f) - def wrapped(*args, **kwargs): - with self: - return f(*args, **kwargs) - return wrapped - return InprogressTracker(self) + def set_to_current_time(self): + '''Set gauge to the current unixtime.''' + self.set(time.time()) - def _samples(self): - with self._lock: - return (('', {}, self._value), ) + def track_inprogress(self): + '''Track inprogress blocks of code or functions. + + Can be used as a function decorator or context manager. + Increments the gauge when the code is entered, + and decrements when it is exited. + ''' + + class InprogressTracker(object): + def __init__(self, gauge): + self._gauge = gauge + + def __enter__(self): + self._gauge.inc() + + def __exit__(self, typ, value, traceback): + self._gauge.dec() + + def __call__(self, f): + @wraps(f) + def wrapped(*args, **kwargs): + with self: + return f(*args, **kwargs) + return wrapped + + return InprogressTracker(self) + + def _samples(self): + with self._lock: + return (('', {}, self._value), ) @_MetricWrapper class Summary(object): - _type = 'summary' - _reserved_labelnames = ['quantile'] + _type = 'summary' + _reserved_labelnames = ['quantile'] - def __init__(self): - self._count = 0.0 - self._sum = 0.0 - self._lock = Lock() + def __init__(self): + self._count = 0.0 + self._sum = 0.0 + self._lock = Lock() - def observe(self, amount): - '''Observe the given amount.''' - with self._lock: - self._count += 1 - self._sum += amount + def observe(self, amount): + '''Observe the given amount.''' + with self._lock: + self._count += 1 + self._sum += amount - def time(self): - '''Time a block of code or function, and observe the duration in seconds. + def time(self): + '''Time a block of code or function, and observe the duration in seconds. - Can be used as a function decorator or context manager. - ''' - class Timer(object): - def __init__(self, summary): - self._summary = summary + Can be used as a function decorator or context manager. + ''' + + class Timer(object): + def __init__(self, summary): + self._summary = summary + + def __enter__(self): + self._start = time.time() - def __enter__(self): - self._start = time.time() + def __exit__(self, typ, value, traceback): + # Time can go backwards. + self._summary.observe(max(time.time() - self._start, 0)) - def __exit__(self, typ, value, traceback): - # Time can go backwards. - self._summary.observe(max(time.time() - self._start, 0)) + def __call__(self, f): + @wraps(f) + def wrapped(*args, **kwargs): + with self: + return f(*args, **kwargs) + return wrapped - def __call__(self, f): - @wraps(f) - def wrapped(*args, **kwargs): - with self: - return f(*args, **kwargs) - return wrapped - return Timer(self) + return Timer(self) - def _samples(self): - with self._lock: - return ( - ('_count', {}, self._count), - ('_sum', {}, self._sum)) + def _samples(self): + with self._lock: + return ( + ('_count', {}, self._count), + ('_sum', {}, self._sum)) def _floatToGoString(d): - if d == _INF: - return '+Inf' - elif d == _MINUS_INF: - return '-Inf' - else: - return repr(d) + if d == _INF: + return '+Inf' + elif d == _MINUS_INF: + return '-Inf' + else: + return repr(d) @_MetricWrapper class Histogram(object): - _type = 'histogram' - _reserved_labelnames = ['histogram'] - - def __init__(self, buckets=(.005, .01, .025, .05, .075, .1, .25, .5, .75, 1.0, 2.5, 5.0, 7.5, 10.0, _INF)): - self._sum = 0.0 - self._lock = Lock() - buckets = [float(b) for b in buckets] - if buckets != sorted(buckets): - # This is probably an error on the part of the user, - # so raise rather than sorting for them. - raise ValueError('Buckets not in sorted order') - if buckets and buckets[-1] != _INF: - buckets.append(_INF) - if len(buckets) < 2: - raise ValueError('Must have at least two buckets') - self._upper_bounds = buckets - self._buckets = [0.0] * len(buckets) - - def observe(self, amount): - '''Observe the given amount.''' - with self._lock: - self._sum += amount - for i, bound in enumerate(self._upper_bounds): - if amount <= bound: - self._buckets[i] += 1 - break - - def time(self): - '''Time a block of code or function, and observe the duration in seconds. - - Can be used as a function decorator or context manager. - ''' - class Timer(object): - def __init__(self, histogram): - self._histogram = histogram - - def __enter__(self): - self._start = time.time() - - def __exit__(self, typ, value, traceback): - # Time can go backwards. - self._histogram.observe(max(time.time() - self._start, 0)) - - def __call__(self, f): - @wraps(f) - def wrapped(*args, **kwargs): - with self: - return f(*args, **kwargs) - return wrapped - return Timer(self) - - def _samples(self): - with self._lock: - samples = [] - acc = 0 - for i, bound in enumerate(self._upper_bounds): - acc += self._buckets[i] - samples.append(('_bucket', {'le': _floatToGoString(bound)}, acc)) - samples.append(('_count', {}, acc)) - samples.append(('_sum', {}, self._sum)) - return tuple(samples) + _type = 'histogram' + _reserved_labelnames = ['histogram'] + + def __init__(self, buckets=(.005, .01, .025, .05, .075, .1, .25, .5, .75, 1.0, 2.5, 5.0, 7.5, 10.0, _INF)): + self._sum = 0.0 + self._lock = Lock() + buckets = [float(b) for b in buckets] + if buckets != sorted(buckets): + # This is probably an error on the part of the user, + # so raise rather than sorting for them. + raise ValueError('Buckets not in sorted order') + if buckets and buckets[-1] != _INF: + buckets.append(_INF) + if len(buckets) < 2: + raise ValueError('Must have at least two buckets') + self._upper_bounds = buckets + self._buckets = [0.0] * len(buckets) + + def observe(self, amount): + '''Observe the given amount.''' + with self._lock: + self._sum += amount + for i, bound in enumerate(self._upper_bounds): + if amount <= bound: + self._buckets[i] += 1 + break + + def time(self): + '''Time a block of code or function, and observe the duration in seconds. + + Can be used as a function decorator or context manager. + ''' + + class Timer(object): + def __init__(self, histogram): + self._histogram = histogram + + def __enter__(self): + self._start = time.time() + + def __exit__(self, typ, value, traceback): + # Time can go backwards. + self._histogram.observe(max(time.time() - self._start, 0)) + + def __call__(self, f): + @wraps(f) + def wrapped(*args, **kwargs): + with self: + return f(*args, **kwargs) + return wrapped + + return Timer(self) + + def _samples(self): + with self._lock: + samples = [] + acc = 0 + for i, bound in enumerate(self._upper_bounds): + acc += self._buckets[i] + samples.append(('_bucket', {'le': _floatToGoString(bound)}, acc)) + samples.append(('_count', {}, acc)) + samples.append(('_sum', {}, self._sum)) + return tuple(samples) CONTENT_TYPE_LATEST = 'text/plain; version=0.0.4; charset=utf-8' @@ -403,55 +411,55 @@ def generate_latest(registry=REGISTRY): '''Returns the metrics from the registry in latest text format as a string.''' output = [] for metric in registry.collect(): - output.append('# HELP {0} {1}'.format( - metric._name, metric._documentation.replace('\\', r'\\').replace('\n', r'\n'))) - output.append('\n# TYPE {0} {1}\n'.format(metric._name, metric._type)) - for name, labels, value in metric._samples: - if labels: - labelstr = '{{{0}}}'.format(','.join( - ['{0}="{1}"'.format( - k, v.replace('\\', r'\\').replace('\n', r'\n').replace('\'', r'\'')) - for k, v in labels.items()])) - else: - labelstr = '' - output.append('{0}{1} {2}\n'.format(name, labelstr, _floatToGoString(value))) + output.append('# HELP {0} {1}'.format( + metric._name, metric._documentation.replace('\\', r'\\').replace('\n', r'\n'))) + output.append('\n# TYPE {0} {1}\n'.format(metric._name, metric._type)) + for name, labels, value in metric._samples: + if labels: + labelstr = '{{{0}}}'.format(','.join( + ['{0}="{1}"'.format( + k, v.replace('\\', r'\\').replace('\n', r'\n').replace('\'', r'\'')) + for k, v in labels.items()])) + else: + labelstr = '' + output.append('{0}{1} {2}\n'.format(name, labelstr, _floatToGoString(value))) return ''.join(output).encode('utf-8') class MetricsHandler(BaseHTTPRequestHandler): - def do_GET(self): - self.send_response(200) - self.send_header('Content-Type', CONTENT_TYPE_LATEST) - self.end_headers() - self.wfile.write(generate_latest(REGISTRY)) + def do_GET(self): + self.send_response(200) + self.send_header('Content-Type', CONTENT_TYPE_LATEST) + self.end_headers() + self.wfile.write(generate_latest(REGISTRY)) def write_to_textfile(path, registry): - '''Write metrics to the given path. + '''Write metrics to the given path. - This is intended for use with the Node exporter textfile collector. - The path must end in .prom for the textfile collector to process it.''' - tmppath = '%s.%s.%s' % (path, os.getpid(), threading.current_thread().ident) - with open(tmppath, 'wb') as f: - f.write(generate_latest(registry)) - # rename(2) is atomic. - os.rename(tmppath, path) + This is intended for use with the Node exporter textfile collector. + The path must end in .prom for the textfile collector to process it.''' + tmppath = '%s.%s.%s' % (path, os.getpid(), threading.current_thread().ident) + with open(tmppath, 'wb') as f: + f.write(generate_latest(registry)) + # rename(2) is atomic. + os.rename(tmppath, path) if __name__ == '__main__': - c = Counter('cc', 'A counter') - c.inc() + c = Counter('cc', 'A counter') + c.inc() - g = Gauge('gg', 'A gauge') - g.set(17) + g = Gauge('gg', 'A gauge') + g.set(17) - s = Summary('ss', 'A summary', ['a', 'b']) - s.labels('c', 'd').observe(17) + s = Summary('ss', 'A summary', ['a', 'b']) + s.labels('c', 'd').observe(17) - h = Histogram('hh', 'A histogram') - h.observe(.6) + h = Histogram('hh', 'A histogram') + h.observe(.6) - from BaseHTTPServer import HTTPServer - server_address = ('', 8000) - httpd = HTTPServer(server_address, MetricsHandler) - httpd.serve_forever() + from BaseHTTPServer import HTTPServer + server_address = ('', 8000) + httpd = HTTPServer(server_address, MetricsHandler) + httpd.serve_forever() diff --git a/tests/test_client.py b/tests/test_client.py index 7f3dfd49..0faaab52 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -6,259 +6,265 @@ class TestCounter(unittest.TestCase): - def setUp(self): - self.registry = CollectorRegistry() - self.counter = Counter('c', 'help', registry=self.registry) - - def test_increment(self): - self.assertEqual(0, self.registry.get_sample_value('c')) - self.counter.inc() - self.assertEqual(1, self.registry.get_sample_value('c')) - self.counter.inc(7) - self.assertEqual(8, self.registry.get_sample_value('c')) - - def test_negative_increment_raises(self): - self.assertRaises(ValueError, self.counter.inc, -1) - - def test_function_decorator(self): - @self.counter.count_exceptions(ValueError) - def f(r): - if r: - raise ValueError - else: - raise TypeError - try: - f(False) - except TypeError: - pass - self.assertEqual(0, self.registry.get_sample_value('c')) - try: - f(True) - except ValueError: - raised = True - self.assertEqual(1, self.registry.get_sample_value('c')) - - def test_block_decorator(self): - with self.counter.count_exceptions(): - pass - self.assertEqual(0, self.registry.get_sample_value('c')) - raised = False - try: - with self.counter.count_exceptions(): - raise ValueError - except: - raised = True - self.assertTrue(raised) - self.assertEqual(1, self.registry.get_sample_value('c')) + def setUp(self): + self.registry = CollectorRegistry() + self.counter = Counter('c', 'help', registry=self.registry) + + def test_increment(self): + self.assertEqual(0, self.registry.get_sample_value('c')) + self.counter.inc() + self.assertEqual(1, self.registry.get_sample_value('c')) + self.counter.inc(7) + self.assertEqual(8, self.registry.get_sample_value('c')) + + def test_negative_increment_raises(self): + self.assertRaises(ValueError, self.counter.inc, -1) + + def test_function_decorator(self): + @self.counter.count_exceptions(ValueError) + def f(r): + if r: + raise ValueError + else: + raise TypeError + + try: + f(False) + except TypeError: + pass + self.assertEqual(0, self.registry.get_sample_value('c')) + + try: + f(True) + except ValueError: + raised = True + self.assertEqual(1, self.registry.get_sample_value('c')) + + def test_block_decorator(self): + with self.counter.count_exceptions(): + pass + self.assertEqual(0, self.registry.get_sample_value('c')) + + raised = False + try: + with self.counter.count_exceptions(): + raise ValueError + except: + raised = True + self.assertTrue(raised) + self.assertEqual(1, self.registry.get_sample_value('c')) class TestGauge(unittest.TestCase): - def setUp(self): - self.registry = CollectorRegistry() - self.gauge = Gauge('g', 'help', registry=self.registry) - - def test_gauge(self): - self.assertEqual(0, self.registry.get_sample_value('g')) - self.gauge.inc() - self.assertEqual(1, self.registry.get_sample_value('g')) - self.gauge.dec(3) - self.assertEqual(-2, self.registry.get_sample_value('g')) - self.gauge.set(9) - self.assertEqual(9, self.registry.get_sample_value('g')) - - def test_function_decorator(self): - self.assertEqual(0, self.registry.get_sample_value('g')) - - @self.gauge.track_inprogress() - def f(): - self.assertEqual(1, self.registry.get_sample_value('g')) - f() - self.assertEqual(0, self.registry.get_sample_value('g')) - - def test_block_decorator(self): - self.assertEqual(0, self.registry.get_sample_value('g')) - with self.gauge.track_inprogress(): - self.assertEqual(1, self.registry.get_sample_value('g')) - self.assertEqual(0, self.registry.get_sample_value('g')) + def setUp(self): + self.registry = CollectorRegistry() + self.gauge = Gauge('g', 'help', registry=self.registry) + + def test_gauge(self): + self.assertEqual(0, self.registry.get_sample_value('g')) + self.gauge.inc() + self.assertEqual(1, self.registry.get_sample_value('g')) + self.gauge.dec(3) + self.assertEqual(-2, self.registry.get_sample_value('g')) + self.gauge.set(9) + self.assertEqual(9, self.registry.get_sample_value('g')) + + def test_function_decorator(self): + self.assertEqual(0, self.registry.get_sample_value('g')) + + @self.gauge.track_inprogress() + def f(): + self.assertEqual(1, self.registry.get_sample_value('g')) + + f() + self.assertEqual(0, self.registry.get_sample_value('g')) + + def test_block_decorator(self): + self.assertEqual(0, self.registry.get_sample_value('g')) + with self.gauge.track_inprogress(): + self.assertEqual(1, self.registry.get_sample_value('g')) + self.assertEqual(0, self.registry.get_sample_value('g')) class TestSummary(unittest.TestCase): - def setUp(self): - self.registry = CollectorRegistry() - self.summary = Summary('s', 'help', registry=self.registry) + def setUp(self): + self.registry = CollectorRegistry() + self.summary = Summary('s', 'help', registry=self.registry) + + def test_summary(self): + self.assertEqual(0, self.registry.get_sample_value('s_count')) + self.assertEqual(0, self.registry.get_sample_value('s_sum')) + self.summary.observe(10) + self.assertEqual(1, self.registry.get_sample_value('s_count')) + self.assertEqual(10, self.registry.get_sample_value('s_sum')) - def test_summary(self): - self.assertEqual(0, self.registry.get_sample_value('s_count')) - self.assertEqual(0, self.registry.get_sample_value('s_sum')) - self.summary.observe(10) - self.assertEqual(1, self.registry.get_sample_value('s_count')) - self.assertEqual(10, self.registry.get_sample_value('s_sum')) + def test_function_decorator(self): + self.assertEqual(0, self.registry.get_sample_value('s_count')) - def test_function_decorator(self): - self.assertEqual(0, self.registry.get_sample_value('s_count')) + @self.summary.time() + def f(): + pass - @self.summary.time() - def f(): - pass - f() - self.assertEqual(1, self.registry.get_sample_value('s_count')) + f() + self.assertEqual(1, self.registry.get_sample_value('s_count')) - def test_block_decorator(self): - self.assertEqual(0, self.registry.get_sample_value('s_count')) - with self.summary.time(): - pass - self.assertEqual(1, self.registry.get_sample_value('s_count')) + def test_block_decorator(self): + self.assertEqual(0, self.registry.get_sample_value('s_count')) + with self.summary.time(): + pass + self.assertEqual(1, self.registry.get_sample_value('s_count')) class TestHistogram(unittest.TestCase): - def setUp(self): - self.registry = CollectorRegistry() - self.histogram = Histogram('h', 'help', registry=self.registry) - self.labels = Histogram('hl', 'help', ['l'], registry=self.registry) - - def test_histogram(self): - self.assertEqual(0, self.registry.get_sample_value('h_bucket', {'le': '1.0'})) - self.assertEqual(0, self.registry.get_sample_value('h_bucket', {'le': '2.5'})) - self.assertEqual(0, self.registry.get_sample_value('h_bucket', {'le': '5.0'})) - self.assertEqual(0, self.registry.get_sample_value('h_bucket', {'le': '+Inf'})) - self.assertEqual(0, self.registry.get_sample_value('h_count')) - self.assertEqual(0, self.registry.get_sample_value('h_sum')) - - self.histogram.observe(2) - self.assertEqual(0, self.registry.get_sample_value('h_bucket', {'le': '1.0'})) - self.assertEqual(1, self.registry.get_sample_value('h_bucket', {'le': '2.5'})) - self.assertEqual(1, self.registry.get_sample_value('h_bucket', {'le': '5.0'})) - self.assertEqual(1, self.registry.get_sample_value('h_bucket', {'le': '+Inf'})) - self.assertEqual(1, self.registry.get_sample_value('h_count')) - self.assertEqual(2, self.registry.get_sample_value('h_sum')) - - self.histogram.observe(2.5) - self.assertEqual(0, self.registry.get_sample_value('h_bucket', {'le': '1.0'})) - self.assertEqual(2, self.registry.get_sample_value('h_bucket', {'le': '2.5'})) - self.assertEqual(2, self.registry.get_sample_value('h_bucket', {'le': '5.0'})) - self.assertEqual(2, self.registry.get_sample_value('h_bucket', {'le': '+Inf'})) - self.assertEqual(2, self.registry.get_sample_value('h_count')) - self.assertEqual(4.5, self.registry.get_sample_value('h_sum')) - - self.histogram.observe(float("inf")) - self.assertEqual(0, self.registry.get_sample_value('h_bucket', {'le': '1.0'})) - self.assertEqual(2, self.registry.get_sample_value('h_bucket', {'le': '2.5'})) - self.assertEqual(2, self.registry.get_sample_value('h_bucket', {'le': '5.0'})) - self.assertEqual(3, self.registry.get_sample_value('h_bucket', {'le': '+Inf'})) - self.assertEqual(3, self.registry.get_sample_value('h_count')) - self.assertEqual(float("inf"), self.registry.get_sample_value('h_sum')) - - def test_setting_buckets(self): - h = Histogram('h', 'help', registry=None, buckets=[0, 1, 2]) - self.assertEqual([0.0, 1.0, 2.0, float("inf")], h._upper_bounds) - - h = Histogram('h', 'help', registry=None, buckets=[0, 1, 2, float("inf")]) - self.assertEqual([0.0, 1.0, 2.0, float("inf")], h._upper_bounds) - - self.assertRaises(ValueError, Histogram, 'h', 'help', registry=None, buckets=[]) - self.assertRaises(ValueError, Histogram, 'h', 'help', registry=None, buckets=[float("inf")]) - self.assertRaises(ValueError, Histogram, 'h', 'help', registry=None, buckets=[3, 1]) - - def test_labels(self): - self.labels.labels('a').observe(2) - self.assertEqual(0, self.registry.get_sample_value('hl_bucket', {'le': '1.0', 'l': 'a'})) - self.assertEqual(1, self.registry.get_sample_value('hl_bucket', {'le': '2.5', 'l': 'a'})) - self.assertEqual(1, self.registry.get_sample_value('hl_bucket', {'le': '5.0', 'l': 'a'})) - self.assertEqual(1, self.registry.get_sample_value('hl_bucket', {'le': '+Inf', 'l': 'a'})) - self.assertEqual(1, self.registry.get_sample_value('hl_count', {'l': 'a'})) - self.assertEqual(2, self.registry.get_sample_value('hl_sum', {'l': 'a'})) - - def test_function_decorator(self): - self.assertEqual(0, self.registry.get_sample_value('h_count')) - self.assertEqual(0, self.registry.get_sample_value('h_bucket', {'le': '+Inf'})) - - @self.histogram.time() - def f(): - pass - f() - self.assertEqual(1, self.registry.get_sample_value('h_count')) - self.assertEqual(1, self.registry.get_sample_value('h_bucket', {'le': '+Inf'})) - - def test_block_decorator(self): - self.assertEqual(0, self.registry.get_sample_value('h_count')) - self.assertEqual(0, self.registry.get_sample_value('h_bucket', {'le': '+Inf'})) - with self.histogram.time(): - pass - self.assertEqual(1, self.registry.get_sample_value('h_count')) - self.assertEqual(1, self.registry.get_sample_value('h_bucket', {'le': '+Inf'})) + def setUp(self): + self.registry = CollectorRegistry() + self.histogram = Histogram('h', 'help', registry=self.registry) + self.labels = Histogram('hl', 'help', ['l'], registry=self.registry) + + def test_histogram(self): + self.assertEqual(0, self.registry.get_sample_value('h_bucket', {'le': '1.0'})) + self.assertEqual(0, self.registry.get_sample_value('h_bucket', {'le': '2.5'})) + self.assertEqual(0, self.registry.get_sample_value('h_bucket', {'le': '5.0'})) + self.assertEqual(0, self.registry.get_sample_value('h_bucket', {'le': '+Inf'})) + self.assertEqual(0, self.registry.get_sample_value('h_count')) + self.assertEqual(0, self.registry.get_sample_value('h_sum')) + + self.histogram.observe(2) + self.assertEqual(0, self.registry.get_sample_value('h_bucket', {'le': '1.0'})) + self.assertEqual(1, self.registry.get_sample_value('h_bucket', {'le': '2.5'})) + self.assertEqual(1, self.registry.get_sample_value('h_bucket', {'le': '5.0'})) + self.assertEqual(1, self.registry.get_sample_value('h_bucket', {'le': '+Inf'})) + self.assertEqual(1, self.registry.get_sample_value('h_count')) + self.assertEqual(2, self.registry.get_sample_value('h_sum')) + + self.histogram.observe(2.5) + self.assertEqual(0, self.registry.get_sample_value('h_bucket', {'le': '1.0'})) + self.assertEqual(2, self.registry.get_sample_value('h_bucket', {'le': '2.5'})) + self.assertEqual(2, self.registry.get_sample_value('h_bucket', {'le': '5.0'})) + self.assertEqual(2, self.registry.get_sample_value('h_bucket', {'le': '+Inf'})) + self.assertEqual(2, self.registry.get_sample_value('h_count')) + self.assertEqual(4.5, self.registry.get_sample_value('h_sum')) + + self.histogram.observe(float("inf")) + self.assertEqual(0, self.registry.get_sample_value('h_bucket', {'le': '1.0'})) + self.assertEqual(2, self.registry.get_sample_value('h_bucket', {'le': '2.5'})) + self.assertEqual(2, self.registry.get_sample_value('h_bucket', {'le': '5.0'})) + self.assertEqual(3, self.registry.get_sample_value('h_bucket', {'le': '+Inf'})) + self.assertEqual(3, self.registry.get_sample_value('h_count')) + self.assertEqual(float("inf"), self.registry.get_sample_value('h_sum')) + + def test_setting_buckets(self): + h = Histogram('h', 'help', registry=None, buckets=[0, 1, 2]) + self.assertEqual([0.0, 1.0, 2.0, float("inf")], h._upper_bounds) + + h = Histogram('h', 'help', registry=None, buckets=[0, 1, 2, float("inf")]) + self.assertEqual([0.0, 1.0, 2.0, float("inf")], h._upper_bounds) + + self.assertRaises(ValueError, Histogram, 'h', 'help', registry=None, buckets=[]) + self.assertRaises(ValueError, Histogram, 'h', 'help', registry=None, buckets=[float("inf")]) + self.assertRaises(ValueError, Histogram, 'h', 'help', registry=None, buckets=[3, 1]) + + def test_labels(self): + self.labels.labels('a').observe(2) + self.assertEqual(0, self.registry.get_sample_value('hl_bucket', {'le': '1.0', 'l': 'a'})) + self.assertEqual(1, self.registry.get_sample_value('hl_bucket', {'le': '2.5', 'l': 'a'})) + self.assertEqual(1, self.registry.get_sample_value('hl_bucket', {'le': '5.0', 'l': 'a'})) + self.assertEqual(1, self.registry.get_sample_value('hl_bucket', {'le': '+Inf', 'l': 'a'})) + self.assertEqual(1, self.registry.get_sample_value('hl_count', {'l': 'a'})) + self.assertEqual(2, self.registry.get_sample_value('hl_sum', {'l': 'a'})) + + def test_function_decorator(self): + self.assertEqual(0, self.registry.get_sample_value('h_count')) + self.assertEqual(0, self.registry.get_sample_value('h_bucket', {'le': '+Inf'})) + + @self.histogram.time() + def f(): + pass + + f() + self.assertEqual(1, self.registry.get_sample_value('h_count')) + self.assertEqual(1, self.registry.get_sample_value('h_bucket', {'le': '+Inf'})) + + def test_block_decorator(self): + self.assertEqual(0, self.registry.get_sample_value('h_count')) + self.assertEqual(0, self.registry.get_sample_value('h_bucket', {'le': '+Inf'})) + with self.histogram.time(): + pass + self.assertEqual(1, self.registry.get_sample_value('h_count')) + self.assertEqual(1, self.registry.get_sample_value('h_bucket', {'le': '+Inf'})) class TestMetricWrapper(unittest.TestCase): - def setUp(self): - self.registry = CollectorRegistry() - self.counter = Counter('c', 'help', labelnames=['l'], registry=self.registry) - self.two_labels = Counter('two', 'help', labelnames=['a', 'b'], registry=self.registry) - - def test_child(self): - self.counter.labels('x').inc() - self.assertEqual(1, self.registry.get_sample_value('c', {'l': 'x'})) - self.two_labels.labels('x', 'y').inc(2) - self.assertEqual(2, self.registry.get_sample_value('two', {'a': 'x', 'b': 'y'})) - - def test_remove(self): - self.counter.labels('x').inc() - self.counter.labels('y').inc(2) - self.assertEqual(1, self.registry.get_sample_value('c', {'l': 'x'})) - self.assertEqual(2, self.registry.get_sample_value('c', {'l': 'y'})) - self.counter.remove('x') - self.assertEqual(None, self.registry.get_sample_value('c', {'l': 'x'})) - self.assertEqual(2, self.registry.get_sample_value('c', {'l': 'y'})) - - def test_incorrect_label_count_raises(self): - self.assertRaises(ValueError, self.counter.labels) - self.assertRaises(ValueError, self.counter.labels, 'a', 'b') - self.assertRaises(ValueError, self.counter.remove) - self.assertRaises(ValueError, self.counter.remove, 'a', 'b') - - def test_namespace_subsystem_concatenated(self): - c = Counter('c', 'help', namespace='a', subsystem='b', registry=self.registry) - c.inc() - self.assertEqual(1, self.registry.get_sample_value('a_b_c')) - - def test_invalid_names_raise(self): - self.assertRaises(ValueError, Counter, '', 'help') - self.assertRaises(ValueError, Counter, '^', 'help') - self.assertRaises(ValueError, Counter, '', 'help', namespace='&') - self.assertRaises(ValueError, Counter, '', 'help', subsystem='(') - self.assertRaises(ValueError, Counter, 'c', '', labelnames=['^']) - self.assertRaises(ValueError, Counter, 'c', '', labelnames=['__reserved']) - self.assertRaises(ValueError, Summary, 'c', '', labelnames=['quantile']) + def setUp(self): + self.registry = CollectorRegistry() + self.counter = Counter('c', 'help', labelnames=['l'], registry=self.registry) + self.two_labels = Counter('two', 'help', labelnames=['a', 'b'], registry=self.registry) + + def test_child(self): + self.counter.labels('x').inc() + self.assertEqual(1, self.registry.get_sample_value('c', {'l': 'x'})) + self.two_labels.labels('x', 'y').inc(2) + self.assertEqual(2, self.registry.get_sample_value('two', {'a': 'x', 'b': 'y'})) + + def test_remove(self): + self.counter.labels('x').inc() + self.counter.labels('y').inc(2) + self.assertEqual(1, self.registry.get_sample_value('c', {'l': 'x'})) + self.assertEqual(2, self.registry.get_sample_value('c', {'l': 'y'})) + self.counter.remove('x') + self.assertEqual(None, self.registry.get_sample_value('c', {'l': 'x'})) + self.assertEqual(2, self.registry.get_sample_value('c', {'l': 'y'})) + + def test_incorrect_label_count_raises(self): + self.assertRaises(ValueError, self.counter.labels) + self.assertRaises(ValueError, self.counter.labels, 'a', 'b') + self.assertRaises(ValueError, self.counter.remove) + self.assertRaises(ValueError, self.counter.remove, 'a', 'b') + + def test_namespace_subsystem_concatenated(self): + c = Counter('c', 'help', namespace='a', subsystem='b', registry=self.registry) + c.inc() + self.assertEqual(1, self.registry.get_sample_value('a_b_c')) + + def test_invalid_names_raise(self): + self.assertRaises(ValueError, Counter, '', 'help') + self.assertRaises(ValueError, Counter, '^', 'help') + self.assertRaises(ValueError, Counter, '', 'help', namespace='&') + self.assertRaises(ValueError, Counter, '', 'help', subsystem='(') + self.assertRaises(ValueError, Counter, 'c', '', labelnames=['^']) + self.assertRaises(ValueError, Counter, 'c', '', labelnames=['__reserved']) + self.assertRaises(ValueError, Summary, 'c', '', labelnames=['quantile']) class TestGenerateText(unittest.TestCase): - def setUp(self): - self.registry = CollectorRegistry() + def setUp(self): + self.registry = CollectorRegistry() - def test_counter(self): - c = Counter('cc', 'A counter', registry=self.registry) - c.inc() - self.assertEqual(b'# HELP cc A counter\n# TYPE cc counter\ncc 1.0\n', generate_latest(self.registry)) + def test_counter(self): + c = Counter('cc', 'A counter', registry=self.registry) + c.inc() + self.assertEqual(b'# HELP cc A counter\n# TYPE cc counter\ncc 1.0\n', generate_latest(self.registry)) - def test_gauge(self): - g = Gauge('gg', 'A gauge', registry=self.registry) - g.set(17) - self.assertEqual(b'# HELP gg A gauge\n# TYPE gg gauge\ngg 17.0\n', generate_latest(self.registry)) + def test_gauge(self): + g = Gauge('gg', 'A gauge', registry=self.registry) + g.set(17) + self.assertEqual(b'# HELP gg A gauge\n# TYPE gg gauge\ngg 17.0\n', generate_latest(self.registry)) - def test_summary(self): - s = Summary('ss', 'A summary', ['a', 'b'], registry=self.registry) - s.labels('c', 'd').observe(17) - self.assertEqual(b'# HELP ss A summary\n# TYPE ss summary\nss_count{a="c",b="d"} 1.0\nss_sum{a="c",b="d"} 17.0\n', generate_latest(self.registry)) + def test_summary(self): + s = Summary('ss', 'A summary', ['a', 'b'], registry=self.registry) + s.labels('c', 'd').observe(17) + self.assertEqual(b'# HELP ss A summary\n# TYPE ss summary\nss_count{a="c",b="d"} 1.0\nss_sum{a="c",b="d"} 17.0\n', generate_latest(self.registry)) - def test_unicode(self): - c = Counter('cc', '\u4500', ['l'], registry=self.registry) - c.labels('\u4500').inc() - self.assertEqual(b'# HELP cc \xe4\x94\x80\n# TYPE cc counter\ncc{l="\xe4\x94\x80"} 1.0\n', generate_latest(self.registry)) + def test_unicode(self): + c = Counter('cc', '\u4500', ['l'], registry=self.registry) + c.labels('\u4500').inc() + self.assertEqual(b'# HELP cc \xe4\x94\x80\n# TYPE cc counter\ncc{l="\xe4\x94\x80"} 1.0\n', generate_latest(self.registry)) - def test_escaping(self): - c = Counter('cc', 'A\ncount\\er', ['a'], registry=self.registry) - c.labels('\\x\n').inc(1) - self.assertEqual(b'# HELP cc A\\ncount\\\\er\n# TYPE cc counter\ncc{a="\\\\x\\n"} 1.0\n', generate_latest(self.registry)) + def test_escaping(self): + c = Counter('cc', 'A\ncount\\er', ['a'], registry=self.registry) + c.labels('\\x\n').inc(1) + self.assertEqual(b'# HELP cc A\\ncount\\\\er\n# TYPE cc counter\ncc{a="\\\\x\\n"} 1.0\n', generate_latest(self.registry)) if __name__ == '__main__': - unittest.main() + unittest.main()