Skip to content
This repository has been archived by the owner on Jul 11, 2022. It is now read-only.

Commit

Permalink
Allow adding 'service' label to Prometheus metrics (#269)
Browse files Browse the repository at this point in the history
* Add support for service label

Signed-off-by: Alex Perez-Pujol <alexperez@paradigmadigital.com>

* Initialize to none for a more idiomatic approach

Signed-off-by: Alex Perez-Pujol <alexperez@paradigmadigital.com>

* Make the name_label parameter optional

Signed-off-by: Alex Perez-Pujol <alexperez@paradigmadigital.com>

* Change label name

Signed-off-by: Alex Perez-Pujol <alexperez@paradigmadigital.com>

* Add tests

Signed-off-by: Alex Perez-Pujol <alexperez@paradigmadigital.com>

* DRY

Signed-off-by: Yuri Shkuro <ys@uber.com>

* Fix readme

Signed-off-by: Yuri Shkuro <ys@uber.com>

* Delint

Signed-off-by: Yuri Shkuro <ys@uber.com>

* Bump code coverage

Signed-off-by: Yuri Shkuro <ys@uber.com>
  • Loading branch information
alexppg authored and yurishkuro committed Dec 6, 2019
1 parent 900c509 commit 51d9027
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 21 deletions.
18 changes: 11 additions & 7 deletions README.md
Expand Up @@ -36,7 +36,7 @@ if __name__ == "__main__":
'param': 1,
},
'logging': True,
},
},
service_name='your-app-name',
validate=True,
)
Expand Down Expand Up @@ -67,7 +67,7 @@ if __name__ == "__main__":
'reporting_port': 'your-reporting-port',
},
'logging': True,
},
},
service_name='your-app-name',
validate=True,
)
Expand All @@ -89,7 +89,7 @@ after all the imports are done.


Also note that using `gevent.monkey` in asyncio-based applications (python 3+) may need to pass current event loop explicitly (see issue #256):

```python
from tornado import ioloop
from jaeger_client import Config
Expand All @@ -115,7 +115,7 @@ If you need to create additional tracers (e.g., to create spans on the client si

#### Prometheus metrics

This module brings a [Prometheus](https://github.com/prometheus/client_python) integration to the internal Jaeger metrics.
This module brings a [Prometheus](https://github.com/prometheus/client_python) integration to the internal Jaeger metrics.
The way to initialize the tracer with Prometheus metrics:

```python
Expand All @@ -125,19 +125,23 @@ config = Config(
config={},
service_name='your-app-name',
validate=True,
metrics_factory=PrometheusMetricsFactory(namespace='your-app-name')
metrics_factory=PrometheusMetricsFactory(service_name_label='your-app-name')
)
tracer = config.initialize_tracer()
```

Note that the optional argument `service_name_label` to the factory constructor
will force it to tag all Jaeger client metrics with a label `service: your-app-name`.
This way you can distinguish Jaeger client metrics produced by different services.

### Development

For development, some parameters can be passed via `config` dictionary, as in the Getting Started example above. For more details please see the [Config class](jaeger_client/config.py).

### WSGI, multi-processing, fork(2)

When using this library in applications that fork child processes to handle individual requests,
such as with [WSGI / PEP 3333](https://wsgi.readthedocs.io/), care must be taken when initializing the tracer.
When using this library in applications that fork child processes to handle individual requests,
such as with [WSGI / PEP 3333](https://wsgi.readthedocs.io/), care must be taken when initializing the tracer.
When Jaeger tracer is initialized, it may start a new background thread. If the process later forks,
it might cause issues or hang the application (due to exclusive lock on the interpreter).
Therefore, it is recommended that the tracer is not initialized until after the child processes
Expand Down
37 changes: 23 additions & 14 deletions jaeger_client/metrics/prometheus.py
Expand Up @@ -21,9 +21,10 @@ class PrometheusMetricsFactory(MetricsFactory):
"""
Provides metrics backed by Prometheus
"""
def __init__(self, namespace=''):
def __init__(self, namespace='', service_name_label=None):
self._cache = defaultdict(object)
self._namespace = namespace
self._service_name_label = service_name_label

def _get_tag_name_list(self, tags):
if tags is None:
Expand All @@ -33,28 +34,36 @@ def _get_tag_name_list(self, tags):
tag_name_list.append(key)
return tag_name_list

def _get_metric(self, metric, name, label_name_list):
cache_key = name + ''.join(label_name_list)
if self._cache.get(cache_key) is None:
self._cache[cache_key] = metric(name=name, documentation=name,
labelnames=label_name_list, namespace=self._namespace)
return self._cache[cache_key]
def _get_metric(self, metricType, name, tags):
if self._service_name_label:
if tags is None:
tags = {'service': self._service_name_label}
else:
tags['service'] = self._service_name_label

def create_counter(self, name, tags=None):
label_name_list = self._get_tag_name_list(tags)
counter = self._get_metric(Counter, name, label_name_list)
cache_key = name + '@@'.join(label_name_list)

metric = self._cache.get(cache_key)
if metric is None:
metric = metricType(name=name, documentation=name,
labelnames=label_name_list, namespace=self._namespace)
self._cache[cache_key] = metric

if tags is not None and len(tags) > 0:
counter = counter.labels(**tags)
metric = metric.labels(**tags)

return metric

def create_counter(self, name, tags=None):
counter = self._get_metric(Counter, name, tags)

def increment(value):
counter.inc(value)
return increment

def create_gauge(self, name, tags=None):
label_name_list = self._get_tag_name_list(tags)
gauge = self._get_metric(Gauge, name, label_name_list)
if tags is not None and len(tags) > 0:
gauge = gauge.labels(**tags)
gauge = self._get_metric(Gauge, name, tags)

def update(value):
gauge.set(value)
Expand Down
27 changes: 27 additions & 0 deletions tests/test_prometheus.py
Expand Up @@ -54,3 +54,30 @@ def test_prometheus_metrics_gauge_without_tags():
gauge(1)
after = REGISTRY.get_sample_value('jaeger:test_gauge_no_tags')
assert 1 == after


def test_prometheus_metrics_metric_with_service_name_label():
metrics = PrometheusMetricsFactory(service_name_label='test')
gauge = metrics.create_gauge(name='jaeger:test_gauge_with_service_name_label')
gauge(1)
gauge_after = REGISTRY.get_sample_value('jaeger:test_gauge_with_service_name_label',
{'service': 'test'})
counter = metrics.create_counter(name='jaeger:test_counter_with_service_name_label',
tags={'x': 'y'})
counter(1)
counter_after = REGISTRY.get_sample_value('jaeger:test_counter_with_service_name_label',
{'service': 'test', 'x': 'y'})
assert 1 == counter_after
assert 1 == gauge_after


def test_prometheus_metrics_metric_without_service_name_label():
metrics = PrometheusMetricsFactory()
gauge = metrics.create_gauge(name='jaeger:test_gauge_without_service_name_label')
gauge(1)
gauge_after = REGISTRY.get_sample_value('jaeger:test_gauge_without_service_name_label')
counter = metrics.create_counter(name='jaeger:test_counter_without_service_name_label')
counter(1)
counter_after = REGISTRY.get_sample_value('jaeger:test_counter_without_service_name_label')
assert 1 == counter_after
assert 1 == gauge_after

0 comments on commit 51d9027

Please sign in to comment.