diff --git a/README.md b/README.md index 6c12bbd6..1eb933cb 100644 --- a/README.md +++ b/README.md @@ -440,13 +440,24 @@ Metrics are pushed over TCP in the Graphite plaintext format. ```python from prometheus_client.bridge.graphite import GraphiteBridge -gb = GraphiteBridge(('graphite.your.org', 2003)) +gb = GraphiteBridge('graphite.your.org', 2003) # Push once. gb.push() # Push every 10 seconds in a daemon thread. gb.start(10.0) ``` +Graphite [tags](https://grafana.com/blog/2018/01/11/graphite-1.1-teaching-an-old-dog-new-tricks/) are also supported. + +```python +from prometheus_client.bridge.graphite import GraphiteBridge + +gb = GraphiteBridge('graphite.your.org', 2003, tags=True) +c = Counter('my_requests_total', 'HTTP Failures', ['method', 'endpoint']) +c.labels('get', '/').inc() +gb.push() +``` + ## Custom Collectors Sometimes it is not possible to directly instrument code, as it is not diff --git a/prometheus_client/bridge/graphite.py b/prometheus_client/bridge/graphite.py index 713d8c09..0cb43aaa 100755 --- a/prometheus_client/bridge/graphite.py +++ b/prometheus_client/bridge/graphite.py @@ -46,9 +46,10 @@ def run(self): class GraphiteBridge(object): - def __init__(self, address, registry=REGISTRY, timeout_seconds=30, _timer=time.time): + def __init__(self, address, registry=REGISTRY, tags=False, timeout_seconds=30, _timer=time.time): self._address = address self._registry = registry + self._tags = tags self._timeout = timeout_seconds self._timer = _timer @@ -63,8 +64,14 @@ def push(self, prefix=''): for metric in self._registry.collect(): for s in metric.samples: if s.labels: - labelstr = '.' + '.'.join( - ['{0}.{1}'.format( + if self._tags: + sep = ';' + fmt = '{0}={1}' + else: + sep = '.' + fmt = '{0}.{1}' + labelstr = sep + sep.join( + [fmt.format( _sanitize(k), _sanitize(v)) for k, v in sorted(s.labels.items())]) else: diff --git a/tests/test_graphite_bridge.py b/tests/test_graphite_bridge.py index 86dc425f..73b38204 100644 --- a/tests/test_graphite_bridge.py +++ b/tests/test_graphite_bridge.py @@ -35,8 +35,11 @@ def run(self): self.t.start() # Explicitly use localhost as the target host, since connecting to 0.0.0.0 fails on Windows - address = ('localhost', server.server_address[1]) - self.gb = GraphiteBridge(address, self.registry, _timer=fake_timer) + self.address = ('localhost', server.server_address[1]) + self.gb = GraphiteBridge(self.address, self.registry, _timer=fake_timer) + + def _use_tags(self): + self.gb = GraphiteBridge(self.address, self.registry, tags=True, _timer=fake_timer) def test_nolabels(self): gauge = Gauge('g', 'help', registry=self.registry) @@ -56,6 +59,16 @@ def test_labels(self): self.assertEqual(b'labels.a.c.b.d 1.0 1434898897\n', self.data) + def test_labels_tags(self): + self._use_tags() + labels = Gauge('labels', 'help', ['a', 'b'], registry=self.registry) + labels.labels('c', 'd').inc() + + self.gb.push() + self.t.join() + + self.assertEqual(b'labels;a=c;b=d 1.0 1434898897\n', self.data) + def test_prefix(self): labels = Gauge('labels', 'help', ['a', 'b'], registry=self.registry) labels.labels('c', 'd').inc() @@ -65,6 +78,16 @@ def test_prefix(self): self.assertEqual(b'pre.fix.labels.a.c.b.d 1.0 1434898897\n', self.data) + def test_prefix_tags(self): + self._use_tags() + labels = Gauge('labels', 'help', ['a', 'b'], registry=self.registry) + labels.labels('c', 'd').inc() + + self.gb.push(prefix='pre.fix') + self.t.join() + + self.assertEqual(b'pre.fix.labels;a=c;b=d 1.0 1434898897\n', self.data) + def test_sanitizing(self): labels = Gauge('labels', 'help', ['a'], registry=self.registry) labels.labels('c.:8').inc() @@ -73,3 +96,13 @@ def test_sanitizing(self): self.t.join() self.assertEqual(b'labels.a.c__8 1.0 1434898897\n', self.data) + + def test_sanitizing_tags(self): + self._use_tags() + labels = Gauge('labels', 'help', ['a'], registry=self.registry) + labels.labels('c.:8').inc() + + self.gb.push() + self.t.join() + + self.assertEqual(b'labels;a=c__8 1.0 1434898897\n', self.data)