diff --git a/README.md b/README.md index b4ec779..ecc4776 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,7 @@ retrieves a value from the `Request` object. [See below](#labels) for examples. `buckets`: accepts an optional list of numbers to use as histogram buckets. The default value is `None`, which will cause the library to fall back on the Prometheus defaults (currently `[0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1.0, 2.5, 5.0, 7.5, 10.0]`). -`skip_paths`: accepts an optional list of paths that will not collect metrics. The default value is `None`, which will cause the library to collect metrics on every requested path. This option is useful to avoid collecting metrics on health check, readiness or liveness probe endpoints. +`skip_paths`: accepts an optional list of paths, or regular expressions for paths, that will not collect metrics. The default value is `None`, which will cause the library to collect metrics on every requested path. This option is useful to avoid collecting metrics on health check, readiness or liveness probe endpoints. `skip_methods`: accepts an optional list of methods that will not collect metrics. The default value is `None`, which will cause the library to collect request metrics with each method. This option is useful to avoid collecting metrics on requests related to the communication description for endpoints. diff --git a/starlette_exporter/middleware.py b/starlette_exporter/middleware.py index 8dfbab3..fa30197 100644 --- a/starlette_exporter/middleware.py +++ b/starlette_exporter/middleware.py @@ -1,5 +1,6 @@ """ Middleware for exporting Prometheus metrics using Starlette """ import logging +import re import time from collections import OrderedDict from contextlib import suppress @@ -101,9 +102,9 @@ def __init__( self.kwargs = {} if buckets is not None: self.kwargs["buckets"] = buckets - self.skip_paths = [] + self.skip_paths: List[re.Pattern] = [] if skip_paths is not None: - self.skip_paths = skip_paths + self.skip_paths = [re.compile(path) for path in skip_paths] self.skip_methods = [] if skip_methods is not None: self.skip_methods = skip_methods @@ -264,7 +265,7 @@ async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: if base_path and path.startswith(base_path): path = path[len(base_path) :] - if path in self.skip_paths or method in self.skip_methods: + if any(pattern.fullmatch(path) for pattern in self.skip_paths) or method in self.skip_methods: await self.app(scope, receive, send) return diff --git a/tests/test_middleware.py b/tests/test_middleware.py index 86659b9..3afd779 100644 --- a/tests/test_middleware.py +++ b/tests/test_middleware.py @@ -391,6 +391,20 @@ def test_skip_paths(self, testapp): metrics = client.get("/metrics").content.decode() assert """path="/health""" not in metrics + def test_skip_paths__re(self, testapp): + """test skip_paths using regular expression""" + client = TestClient(testapp(skip_paths=[r"/h.*"])) + client.get("/health") + metrics = client.get("/metrics").content.decode() + assert """path="/health""" not in metrics + + def test_skip_paths__re_partial(self, testapp): + """test skip_paths using regular expression""" + client = TestClient(testapp(skip_paths=[r"/h"])) + client.get("/health") + metrics = client.get("/metrics").content.decode() + assert """path="/health""" in metrics + def test_skip_methods(self, testapp): """test that requests doesn't appear in the counter""" client = TestClient(testapp(skip_methods=["POST"]))