MCPcopy Create free account
hub / github.com/encode/starlette / OpenTelemetryMiddleware

Class OpenTelemetryMiddleware

starlette/middleware/opentelemetry.py:18–126  ·  view source on GitHub ↗

Create OpenTelemetry server spans for incoming HTTP requests. This middleware is experimental. Its API and emitted telemetry may change in minor releases without a deprecation period. Args: app: The ASGI application to wrap. excluded_urls: Regular expressions matched ag

Source from the content-addressed store, hash-verified

16
17
18class OpenTelemetryMiddleware:
19 """Create OpenTelemetry server spans for incoming HTTP requests.
20
21 This middleware is experimental. Its API and emitted telemetry may change in minor
22 releases without a deprecation period.
23
24 Args:
25 app: The ASGI application to wrap.
26 excluded_urls: Regular expressions matched against the full request URL.
27 Pass a comma-separated string or a sequence. By default, exclude no URLs.
28 tracer_provider: Optional tracer provider. If omitted, use the global tracer provider.
29 meter_provider: Optional meter provider. If omitted, use the global meter provider.
30 """
31
32 def __init__(
33 self,
34 app: ASGIApp,
35 *,
36 excluded_urls: str | Sequence[str] = (),
37 tracer_provider: trace.TracerProvider | None = None,
38 meter_provider: metrics.MeterProvider | None = None,
39 ) -> None:
40 self.app = app
41 self._meter_provider = meter_provider
42 if isinstance(excluded_urls, str):
43 excluded_urls = [pattern.strip() for pattern in excluded_urls.split(",")] if excluded_urls else ()
44 self._excluded_urls = tuple(re.compile(pattern) for pattern in excluded_urls)
45 self._tracer_provider = tracer_provider or trace.get_tracer_provider()
46
47 async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
48 if scope["type"] != "http" or scope.get("starlette.opentelemetry"):
49 return await self.app(scope, receive, send)
50
51 scope["starlette.opentelemetry"] = True
52 try:
53 tracer_provider = self._tracer_provider
54 if isinstance(tracer_provider, trace.NoOpTracerProvider):
55 return await self.app(scope, receive, send)
56
57 url = URL(scope=scope)
58 if any(pattern.search(str(url)) for pattern in self._excluded_urls):
59 return await self.app(scope, receive, send)
60
61 original_method = scope.get("method", "")
62 method = original_method.upper()
63
64 headers: dict[str, list[str]] = {}
65 for name, value in scope.get("headers", []):
66 headers.setdefault(name.decode("latin-1").lower(), []).append(value.decode("latin-1"))
67
68 attributes: dict[str, str | int] = {
69 "http.request.method": method,
70 "url.path": scope.get("path", ""),
71 "url.scheme": scope.get("scheme", "http"),
72 }
73 if original_method != method:
74 attributes["http.request.method_original"] = original_method
75 if url.query:

Calls

no outgoing calls