mirror of
https://github.com/open-telemetry/opentelemetry-python-contrib.git
synced 2025-07-30 05:32:30 +08:00
Add ability to exclude some routes in fastapi and starlette (#237)
This commit is contained in:
@ -64,11 +64,7 @@ carrier_getter = CarrierGetter()
|
||||
def collect_request_attributes(scope):
|
||||
"""Collects HTTP request attributes from the ASGI scope and returns a
|
||||
dictionary to be used as span creation attributes."""
|
||||
server = scope.get("server") or ["0.0.0.0", 80]
|
||||
port = server[1]
|
||||
server_host = server[0] + (":" + str(port) if port != 80 else "")
|
||||
full_path = scope.get("root_path", "") + scope.get("path", "")
|
||||
http_url = scope.get("scheme", "http") + "://" + server_host + full_path
|
||||
server_host, port, http_url = get_host_port_url_tuple(scope)
|
||||
query_string = scope.get("query_string")
|
||||
if query_string and http_url:
|
||||
if isinstance(query_string, bytes):
|
||||
@ -105,6 +101,17 @@ def collect_request_attributes(scope):
|
||||
return result
|
||||
|
||||
|
||||
def get_host_port_url_tuple(scope):
|
||||
"""Returns (host, port, full_url) tuple.
|
||||
"""
|
||||
server = scope.get("server") or ["0.0.0.0", 80]
|
||||
port = server[1]
|
||||
server_host = server[0] + (":" + str(port) if port != 80 else "")
|
||||
full_path = scope.get("root_path", "") + scope.get("path", "")
|
||||
http_url = scope.get("scheme", "http") + "://" + server_host + full_path
|
||||
return server_host, port, http_url
|
||||
|
||||
|
||||
def set_status_code(span, status_code):
|
||||
"""Adds HTTP response attributes to span using the status_code argument."""
|
||||
if not span.is_recording():
|
||||
@ -152,12 +159,13 @@ class OpenTelemetryMiddleware:
|
||||
Optional: Defaults to get_default_span_details.
|
||||
"""
|
||||
|
||||
def __init__(self, app, span_details_callback=None):
|
||||
def __init__(self, app, excluded_urls=None, span_details_callback=None):
|
||||
self.app = guarantee_single_callable(app)
|
||||
self.tracer = trace.get_tracer(__name__, __version__)
|
||||
self.span_details_callback = (
|
||||
span_details_callback or get_default_span_details
|
||||
)
|
||||
self.excluded_urls = excluded_urls
|
||||
|
||||
async def __call__(self, scope, receive, send):
|
||||
"""The ASGI application
|
||||
@ -170,6 +178,10 @@ class OpenTelemetryMiddleware:
|
||||
if scope["type"] not in ("http", "websocket"):
|
||||
return await self.app(scope, receive, send)
|
||||
|
||||
_, _, url = get_host_port_url_tuple(scope)
|
||||
if self.excluded_urls and self.excluded_urls.url_disabled(url):
|
||||
return await self.app(scope, receive, send)
|
||||
|
||||
token = context.attach(propagators.extract(carrier_getter, scope))
|
||||
span_name, additional_attributes = self.span_details_callback(scope)
|
||||
|
||||
|
@ -2,6 +2,9 @@
|
||||
|
||||
## Unreleased
|
||||
|
||||
- Added support for excluding some routes with env var `OTEL_PYTHON_FASTAPI_EXCLUDED_URLS`
|
||||
([#237](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/237))
|
||||
|
||||
## Version 0.11b0
|
||||
|
||||
Released 2020-07-28
|
||||
|
@ -19,6 +19,21 @@ Installation
|
||||
|
||||
pip install opentelemetry-instrumentation-fastapi
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
Exclude lists
|
||||
*************
|
||||
To exclude certain URLs from being tracked, set the environment variable ``OTEL_PYTHON_FASTAPI_EXCLUDED_URLS`` with comma delimited regexes representing which URLs to exclude.
|
||||
|
||||
For example,
|
||||
|
||||
::
|
||||
|
||||
export OTEL_PYTHON_FASTAPI_EXCLUDED_URLS="client/.*/info,healthcheck"
|
||||
|
||||
will exclude requests such as ``https://site/client/123/info`` and ``https://site/xyz/healthcheck``.
|
||||
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
@ -16,10 +16,13 @@ from typing import Optional
|
||||
import fastapi
|
||||
from starlette.routing import Match
|
||||
|
||||
from opentelemetry.configuration import Configuration
|
||||
from opentelemetry.instrumentation.asgi import OpenTelemetryMiddleware
|
||||
from opentelemetry.instrumentation.fastapi.version import __version__ # noqa
|
||||
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
|
||||
|
||||
_excluded_urls = Configuration()._excluded_urls("fastapi")
|
||||
|
||||
|
||||
class FastAPIInstrumentor(BaseInstrumentor):
|
||||
"""An instrumentor for FastAPI
|
||||
@ -36,6 +39,7 @@ class FastAPIInstrumentor(BaseInstrumentor):
|
||||
if not getattr(app, "is_instrumented_by_opentelemetry", False):
|
||||
app.add_middleware(
|
||||
OpenTelemetryMiddleware,
|
||||
excluded_urls=_excluded_urls,
|
||||
span_details_callback=_get_route_details,
|
||||
)
|
||||
app.is_instrumented_by_opentelemetry = True
|
||||
@ -52,7 +56,9 @@ class _InstrumentedFastAPI(fastapi.FastAPI):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.add_middleware(
|
||||
OpenTelemetryMiddleware, span_details_callback=_get_route_details
|
||||
OpenTelemetryMiddleware,
|
||||
excluded_urls=_excluded_urls,
|
||||
span_details_callback=_get_route_details,
|
||||
)
|
||||
|
||||
|
||||
|
@ -13,11 +13,13 @@
|
||||
# limitations under the License.
|
||||
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
|
||||
import fastapi
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
import opentelemetry.instrumentation.fastapi as otel_fastapi
|
||||
from opentelemetry.configuration import Configuration
|
||||
from opentelemetry.test.test_base import TestBase
|
||||
|
||||
|
||||
@ -29,10 +31,26 @@ class TestFastAPIManualInstrumentation(TestBase):
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
Configuration()._reset()
|
||||
self.env_patch = patch.dict(
|
||||
"os.environ",
|
||||
{"OTEL_PYTHON_FASTAPI_EXCLUDED_URLS": "/exclude/123,healthzz"},
|
||||
)
|
||||
self.env_patch.start()
|
||||
self.exclude_patch = patch(
|
||||
"opentelemetry.instrumentation.fastapi._excluded_urls",
|
||||
Configuration()._excluded_urls("fastapi"),
|
||||
)
|
||||
self.exclude_patch.start()
|
||||
self._instrumentor = otel_fastapi.FastAPIInstrumentor()
|
||||
self._app = self._create_app()
|
||||
self._client = TestClient(self._app)
|
||||
|
||||
def tearDown(self):
|
||||
super().tearDown()
|
||||
self.env_patch.stop()
|
||||
self.exclude_patch.stop()
|
||||
|
||||
def test_basic_fastapi_call(self):
|
||||
self._client.get("/foobar")
|
||||
spans = self.memory_exporter.get_finished_spans()
|
||||
@ -54,6 +72,15 @@ class TestFastAPIManualInstrumentation(TestBase):
|
||||
# the asgi instrumentation is successfully feeding though.
|
||||
self.assertEqual(spans[-1].attributes["http.flavor"], "1.1")
|
||||
|
||||
def test_fastapi_excluded_urls(self):
|
||||
"""Ensure that given fastapi routes are excluded."""
|
||||
self._client.get("/exclude/123")
|
||||
spans = self.memory_exporter.get_finished_spans()
|
||||
self.assertEqual(len(spans), 0)
|
||||
self._client.get("/healthzz")
|
||||
spans = self.memory_exporter.get_finished_spans()
|
||||
self.assertEqual(len(spans), 0)
|
||||
|
||||
@staticmethod
|
||||
def _create_fastapi_app():
|
||||
app = fastapi.FastAPI()
|
||||
@ -66,6 +93,14 @@ class TestFastAPIManualInstrumentation(TestBase):
|
||||
async def _(username: str):
|
||||
return {"message": username}
|
||||
|
||||
@app.get("/exclude/{param}")
|
||||
async def _(param: str):
|
||||
return {"message": param}
|
||||
|
||||
@app.get("/healthzz")
|
||||
async def health():
|
||||
return {"message": "ok"}
|
||||
|
||||
return app
|
||||
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
# Changelog
|
||||
|
||||
## Unreleased
|
||||
- Added support for excluding some routes with env var `OTEL_PYTHON_STARLETTE_EXCLUDED_URLS`
|
||||
([#237](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/237))
|
||||
|
||||
## Version 0.10b0
|
||||
|
||||
|
@ -19,6 +19,21 @@ Installation
|
||||
|
||||
pip install opentelemetry-instrumentation-starlette
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
Exclude lists
|
||||
*************
|
||||
To exclude certain URLs from being tracked, set the environment variable ``OTEL_PYTHON_STARLETTE_EXCLUDED_URLS`` with comma delimited regexes representing which URLs to exclude.
|
||||
|
||||
For example,
|
||||
|
||||
::
|
||||
|
||||
export OTEL_PYTHON_STARLETTE_EXCLUDED_URLS="client/.*/info,healthcheck"
|
||||
|
||||
will exclude requests such as ``https://site/client/123/info`` and ``https://site/xyz/healthcheck``.
|
||||
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
@ -16,10 +16,13 @@ from typing import Optional
|
||||
from starlette import applications
|
||||
from starlette.routing import Match
|
||||
|
||||
from opentelemetry.configuration import Configuration
|
||||
from opentelemetry.instrumentation.asgi import OpenTelemetryMiddleware
|
||||
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
|
||||
from opentelemetry.instrumentation.starlette.version import __version__ # noqa
|
||||
|
||||
_excluded_urls = Configuration()._excluded_urls("starlette")
|
||||
|
||||
|
||||
class StarletteInstrumentor(BaseInstrumentor):
|
||||
"""An instrumentor for starlette
|
||||
@ -36,6 +39,7 @@ class StarletteInstrumentor(BaseInstrumentor):
|
||||
if not getattr(app, "is_instrumented_by_opentelemetry", False):
|
||||
app.add_middleware(
|
||||
OpenTelemetryMiddleware,
|
||||
excluded_urls=_excluded_urls,
|
||||
span_details_callback=_get_route_details,
|
||||
)
|
||||
app.is_instrumented_by_opentelemetry = True
|
||||
@ -52,7 +56,9 @@ class _InstrumentedStarlette(applications.Starlette):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.add_middleware(
|
||||
OpenTelemetryMiddleware, span_details_callback=_get_route_details
|
||||
OpenTelemetryMiddleware,
|
||||
excluded_urls=_excluded_urls,
|
||||
span_details_callback=_get_route_details,
|
||||
)
|
||||
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
# limitations under the License.
|
||||
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
|
||||
from starlette import applications
|
||||
from starlette.responses import PlainTextResponse
|
||||
@ -20,6 +21,7 @@ from starlette.routing import Route
|
||||
from starlette.testclient import TestClient
|
||||
|
||||
import opentelemetry.instrumentation.starlette as otel_starlette
|
||||
from opentelemetry.configuration import Configuration
|
||||
from opentelemetry.test.test_base import TestBase
|
||||
|
||||
|
||||
@ -31,10 +33,26 @@ class TestStarletteManualInstrumentation(TestBase):
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
Configuration()._reset()
|
||||
self.env_patch = patch.dict(
|
||||
"os.environ",
|
||||
{"OTEL_PYTHON_STARLETTE_EXCLUDED_URLS": "/exclude/123,healthzz"},
|
||||
)
|
||||
self.env_patch.start()
|
||||
self.exclude_patch = patch(
|
||||
"opentelemetry.instrumentation.starlette._excluded_urls",
|
||||
Configuration()._excluded_urls("starlette"),
|
||||
)
|
||||
self.exclude_patch.start()
|
||||
self._instrumentor = otel_starlette.StarletteInstrumentor()
|
||||
self._app = self._create_app()
|
||||
self._client = TestClient(self._app)
|
||||
|
||||
def tearDown(self):
|
||||
super().tearDown()
|
||||
self.env_patch.stop()
|
||||
self.exclude_patch.stop()
|
||||
|
||||
def test_basic_starlette_call(self):
|
||||
self._client.get("/foobar")
|
||||
spans = self.memory_exporter.get_finished_spans()
|
||||
@ -56,13 +74,26 @@ class TestStarletteManualInstrumentation(TestBase):
|
||||
# the asgi instrumentation is successfully feeding though.
|
||||
self.assertEqual(spans[-1].attributes["http.flavor"], "1.1")
|
||||
|
||||
def test_starlette_excluded_urls(self):
|
||||
"""Ensure that givem starlette routes are excluded."""
|
||||
self._client.get("/healthzz")
|
||||
spans = self.memory_exporter.get_finished_spans()
|
||||
self.assertEqual(len(spans), 0)
|
||||
|
||||
@staticmethod
|
||||
def _create_starlette_app():
|
||||
def home(_):
|
||||
return PlainTextResponse("hi")
|
||||
|
||||
def health(_):
|
||||
return PlainTextResponse("ok")
|
||||
|
||||
app = applications.Starlette(
|
||||
routes=[Route("/foobar", home), Route("/user/{username}", home)]
|
||||
routes=[
|
||||
Route("/foobar", home),
|
||||
Route("/user/{username}", home),
|
||||
Route("/healthzz", health),
|
||||
]
|
||||
)
|
||||
return app
|
||||
|
||||
|
Reference in New Issue
Block a user