Skip to content

Commit

Permalink
Introduce exclude_user_defaults to apply excluded_paths to user-defin…
Browse files Browse the repository at this point in the history
…ed default metrics

Fixes 108
  • Loading branch information
Viktor Adam committed Oct 1, 2021
1 parent e2702b1 commit 73efe45
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 18 deletions.
11 changes: 6 additions & 5 deletions README.md
Expand Up @@ -24,7 +24,7 @@ or paste it into requirements.txt:
prometheus-flask-exporter
# or with specific version number
prometheus-flask-exporter==0.18.2
prometheus-flask-exporter==0.18.3
```
and then install dependencies from requirements.txt file as usual:
```
Expand Down Expand Up @@ -150,10 +150,11 @@ You can avoid recording metrics on individual endpoints
by decorating them with `@metrics.do_not_track()`, or use the
`excluded_paths` argument when creating the `PrometheusMetrics` instance
that takes a regular expression (either a single string, or a list) and
matching paths will be excluded. If you have functions that are inherited
or otherwise get metrics collected that you don't want, you can use
`@metrics.exclude_all_metrics()` to exclude both default and non-default
metrics being collected from it.
matching paths will be excluded. These apply to both built-in and user-defined
default metrics, unless you disable it by setting the `exclude_user_defaults`
argument to `False`. If you have functions that are inherited or otherwise get
metrics collected that you don't want, you can use `@metrics.exclude_all_metrics()`
to exclude both default and non-default metrics being collected from it.

## Configuration

Expand Down
37 changes: 26 additions & 11 deletions prometheus_flask_exporter/__init__.py
@@ -1,18 +1,18 @@
import functools
import inspect
import os
import re
import sys
import inspect
import warnings
import functools
import threading
import warnings
from timeit import default_timer

from flask import request, make_response, current_app
from flask import Flask, Response
from flask import request, make_response, current_app
from flask.views import MethodViewType
from werkzeug.serving import is_running_from_reloader
from prometheus_client import Counter, Histogram, Gauge, Summary
from prometheus_client.exposition import choose_encoder
from werkzeug.serving import is_running_from_reloader

if sys.version_info[0:2] >= (3, 4):
# Python v3.4+ has a built-in has __wrapped__ attribute
Expand Down Expand Up @@ -102,12 +102,18 @@ def echo_status(status):
- Without an argument, possibly to use with the Flask `request` object
"""

def __init__(self, app, path='/metrics',
export_defaults=True, defaults_prefix='flask',
group_by='path', buckets=None,
def __init__(self, app,
path='/metrics',
export_defaults=True,
defaults_prefix='flask',
group_by='path',
buckets=None,
default_latency_as_histogram=True,
default_labels=None, response_converter=None,
excluded_paths=None, metrics_decorator=None,
default_labels=None,
response_converter=None,
excluded_paths=None,
exclude_user_defaults=True,
metrics_decorator=None,
registry=None, **kwargs):
"""
Create a new Prometheus metrics export configuration.
Expand Down Expand Up @@ -135,6 +141,8 @@ def __init__(self, app, path='/metrics',
metrics endpoint, takes a function and needs to return a function
:param excluded_paths: regular expression(s) as a string or
a list of strings for paths to exclude from tracking
:param exclude_user_defaults: also apply the `excluded_paths`
exclusions to user-defined defaults (not only built-in ones)
:param registry: the Prometheus Registry to use
"""

Expand Down Expand Up @@ -194,6 +202,8 @@ def __init__(self, app, path='/metrics',
else:
self.excluded_paths = None

self.exclude_user_defaults = exclude_user_defaults

if app is not None:
self.init_app(app)

Expand Down Expand Up @@ -623,6 +633,11 @@ def get_metric(response):
def decorator(f):
@wraps(f)
def func(*args, **kwargs):
if self.exclude_user_defaults and self.excluded_paths:
# exclude based on default excludes
if any(pattern.match(request.path) for pattern in self.excluded_paths):
return f(*args, **kwargs)

if before:
metric = get_metric(None)
before(metric)
Expand Down Expand Up @@ -915,4 +930,4 @@ def _make_response(response):
return _make_response


__version__ = '0.18.2'
__version__ = '0.18.3'
4 changes: 2 additions & 2 deletions setup.py
Expand Up @@ -6,15 +6,15 @@
setup(
name='prometheus_flask_exporter',
packages=['prometheus_flask_exporter'],
version='0.18.2',
version='0.18.3',
description='Prometheus metrics exporter for Flask',
long_description=long_description,
long_description_content_type='text/markdown',
license='MIT',
author='Viktor Adam',
author_email='rycus86@gmail.com',
url='https://github.com/rycus86/prometheus_flask_exporter',
download_url='https://github.com/rycus86/prometheus_flask_exporter/archive/0.18.2.tar.gz',
download_url='https://github.com/rycus86/prometheus_flask_exporter/archive/0.18.3.tar.gz',
keywords=['prometheus', 'flask', 'monitoring', 'exporter'],
classifiers=[
'Development Status :: 4 - Beta',
Expand Down
37 changes: 37 additions & 0 deletions tests/test_endpoint.py
Expand Up @@ -406,3 +406,40 @@ def excluded_two():
'flask_http_request_duration_seconds_count',
('method', 'GET'), ('status', 200), ('path', '/exc/two')
)

def test_exclude_paths_from_user_metrics(self):
metrics = self.metrics(excluded_paths='/excluded', exclude_user_defaults=True)

@self.app.route('/included')
def included():
return 'OK'

@self.app.route('/excluded')
def excluded():
return 'OK'

metrics.register_default(
metrics.counter(
name='by_path_counter',
description='Request count by path',
labels={'path': lambda: request.path}
)
)

for _ in range(5):
self.client.get('/included')
self.client.get('/excluded')

self.assertMetric(
'flask_http_request_total', 5.0,
('method', 'GET'), ('status', 200)
)

self.assertMetric(
'by_path_counter_total', 5.0,
('path', '/included')
)
self.assertAbsent(
'by_path_counter_total',
('path', '/excluded')
)

0 comments on commit 73efe45

Please sign in to comment.