-
-
Notifications
You must be signed in to change notification settings - Fork 490
feat: deprecate contrib.opentelemetry
#4477
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
cofin
wants to merge
6
commits into
main
Choose a base branch
from
feat/otel-contrib-deprecate
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
7f5dfd7
chore: silence a few configurations folder
cofin 3f19b88
feat(opentelemetry): deprecate contrib module and migrate to plugins
cofin f37202b
chore: linting and formatting
cofin 1e74fba
fix(docs): corrected references
cofin 08ad466
fix: additional doc updates
cofin 44a0d90
fix: final remaining references
cofin File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -48,3 +48,9 @@ __pypackages__/ | |
| certs/ | ||
| pdm.toml | ||
| .zed | ||
| .claude | ||
| .gemini | ||
| specs/ | ||
| CLAUDE.md | ||
| AGENTS.md | ||
| GEMINI.md | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,6 +12,7 @@ plugins | |
| attrs | ||
| flash_messages | ||
| htmx | ||
| opentelemetry | ||
| problem_details | ||
| prometheus | ||
| pydantic | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| opentelemetry | ||
| ============= | ||
|
|
||
| .. automodule:: litestar.plugins.opentelemetry | ||
| :members: | ||
|
|
||
| .. autoclass:: litestar.plugins.opentelemetry.config.OpenTelemetryHookHandler |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,46 @@ | ||
| from .config import OpenTelemetryConfig | ||
| from .middleware import OpenTelemetryInstrumentationMiddleware | ||
| from .plugin import OpenTelemetryPlugin | ||
| from __future__ import annotations | ||
|
|
||
| from typing import TYPE_CHECKING | ||
|
|
||
| from litestar.utils import warn_deprecation | ||
|
|
||
| warn_deprecation( | ||
| deprecated_name="litestar.contrib.opentelemetry", | ||
| version="2.18.0", | ||
| kind="import", | ||
| removal_in="3.0.0", | ||
| info="The 'litestar.contrib.opentelemetry' module is deprecated. " | ||
| "Please import from 'litestar.plugins.opentelemetry' instead.", | ||
| ) | ||
|
|
||
| __all__ = ( | ||
| "OpenTelemetryConfig", | ||
| "OpenTelemetryInstrumentationMiddleware", | ||
| "OpenTelemetryPlugin", | ||
| ) | ||
|
|
||
|
|
||
| def __getattr__(attr_name: str) -> object: | ||
| if attr_name in __all__: | ||
| from litestar.plugins import opentelemetry | ||
|
|
||
| value = globals()[attr_name] = getattr(opentelemetry, attr_name) | ||
| warn_deprecation( | ||
| deprecated_name=f"litestar.contrib.opentelemetry.{attr_name}", | ||
| version="2.18.0", | ||
| kind="import", | ||
| removal_in="3.0.0", | ||
| info=f"importing {attr_name} from 'litestar.contrib.opentelemetry' is deprecated, " | ||
| f"import from 'litestar.plugins.opentelemetry' instead", | ||
| ) | ||
| return value | ||
|
|
||
| raise AttributeError(f"module {__name__!r} has no attribute {attr_name!r}") # pragma: no cover | ||
|
|
||
|
|
||
| if TYPE_CHECKING: | ||
| from litestar.plugins.opentelemetry import ( | ||
| OpenTelemetryConfig, | ||
| OpenTelemetryInstrumentationMiddleware, | ||
| OpenTelemetryPlugin, | ||
| ) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| from .config import OpenTelemetryConfig | ||
| from .middleware import OpenTelemetryInstrumentationMiddleware | ||
| from .plugin import OpenTelemetryPlugin | ||
|
|
||
| __all__ = ( | ||
| "OpenTelemetryConfig", | ||
| "OpenTelemetryInstrumentationMiddleware", | ||
| "OpenTelemetryPlugin", | ||
| ) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| from __future__ import annotations | ||
|
|
||
| from typing import TYPE_CHECKING, Any | ||
|
|
||
| from litestar.exceptions import MissingDependencyException | ||
|
|
||
| __all__ = ("get_route_details_from_scope",) | ||
|
|
||
|
|
||
| try: | ||
| import opentelemetry # noqa: F401 | ||
| except ImportError as e: | ||
| raise MissingDependencyException("opentelemetry") from e | ||
|
|
||
| from opentelemetry.semconv.trace import SpanAttributes | ||
|
|
||
| if TYPE_CHECKING: | ||
| from litestar.types import Scope | ||
|
|
||
|
|
||
| def get_route_details_from_scope(scope: Scope) -> tuple[str, dict[Any, str]]: | ||
| """Retrieve the span name and attributes from the ASGI scope. | ||
|
|
||
| Args: | ||
| scope: The ASGI scope instance. | ||
|
|
||
| Returns: | ||
| A tuple of the span name and a dict of attrs. | ||
| """ | ||
|
|
||
| path = scope.get("path", "").strip() | ||
| method = str(scope.get("method", "")).strip() | ||
|
|
||
| if method and path: # http | ||
| return f"{method} {path}", {SpanAttributes.HTTP_ROUTE: f"{method} {path}"} | ||
|
|
||
| return path, {SpanAttributes.HTTP_ROUTE: path} # websocket |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,102 @@ | ||
| from __future__ import annotations | ||
|
|
||
| from dataclasses import dataclass, field | ||
| from typing import TYPE_CHECKING, Any, Callable | ||
|
|
||
| from litestar.exceptions import MissingDependencyException | ||
| from litestar.middleware.base import DefineMiddleware | ||
| from litestar.plugins.opentelemetry._utils import get_route_details_from_scope | ||
| from litestar.plugins.opentelemetry.middleware import ( | ||
| OpenTelemetryInstrumentationMiddleware, | ||
| ) | ||
|
|
||
| __all__ = ("OpenTelemetryConfig",) | ||
|
|
||
|
|
||
| try: | ||
| import opentelemetry # noqa: F401 | ||
| except ImportError as e: | ||
| raise MissingDependencyException("opentelemetry") from e | ||
|
|
||
|
|
||
| from opentelemetry.trace import Span, TracerProvider # pyright: ignore | ||
|
|
||
| if TYPE_CHECKING: | ||
| from opentelemetry.metrics import Meter, MeterProvider | ||
|
|
||
| from litestar.types import Scope, Scopes | ||
|
|
||
| OpenTelemetryHookHandler = Callable[[Span, dict], None] | ||
|
|
||
|
|
||
| @dataclass | ||
| class OpenTelemetryConfig: | ||
| """Configuration class for the OpenTelemetry middleware. | ||
|
|
||
| Consult the [OpenTelemetry ASGI documentation](https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/asgi/asgi.html) for more info about the configuration options. | ||
| """ | ||
|
|
||
| scope_span_details_extractor: Callable[[Scope], tuple[str, dict[str, Any]]] = field( | ||
| default=get_route_details_from_scope | ||
| ) | ||
| """Callback which should return a string and a tuple, representing the desired default span name and a dictionary | ||
| with any additional span attributes to set. | ||
| """ | ||
| server_request_hook_handler: OpenTelemetryHookHandler | None = field(default=None) | ||
| """Optional callback which is called with the server span and ASGI scope object for every incoming request.""" | ||
| client_request_hook_handler: OpenTelemetryHookHandler | None = field(default=None) | ||
| """Optional callback which is called with the internal span and an ASGI scope which is sent as a dictionary for when | ||
| the method receive is called. | ||
| """ | ||
| client_response_hook_handler: OpenTelemetryHookHandler | None = field(default=None) | ||
| """Optional callback which is called with the internal span and an ASGI event which is sent as a dictionary for when | ||
| the method send is called. | ||
| """ | ||
| meter_provider: MeterProvider | None = field(default=None) | ||
| """Optional meter provider to use. | ||
|
|
||
| If omitted the current globally configured one is used. | ||
| """ | ||
| tracer_provider: TracerProvider | None = field(default=None) | ||
| """Optional tracer provider to use. | ||
|
|
||
| If omitted the current globally configured one is used. | ||
| """ | ||
| meter: Meter | None = field(default=None) | ||
| """Optional meter to use. | ||
|
|
||
| If omitted the provided meter provider or the global one will be used. | ||
| """ | ||
| exclude: str | list[str] | None = field(default=None) | ||
| """A pattern or list of patterns to skip in the Allowed Hosts middleware.""" | ||
| exclude_opt_key: str | None = field(default=None) | ||
| """An identifier to use on routes to disable hosts check for a particular route.""" | ||
| exclude_urls_env_key: str = "LITESTAR" | ||
| """Key to use when checking whether a list of excluded urls is passed via ENV. | ||
|
|
||
| OpenTelemetry supports excluding urls by passing an env in the format '{exclude_urls_env_key}_EXCLUDED_URLS'. With | ||
| the default being ``LITESTAR_EXCLUDED_URLS``. | ||
| """ | ||
| scopes: Scopes | None = field(default=None) | ||
| """ASGI scopes processed by the middleware, if None both ``http`` and ``websocket`` will be processed.""" | ||
| middleware_class: type[OpenTelemetryInstrumentationMiddleware] = field( | ||
| default=OpenTelemetryInstrumentationMiddleware | ||
| ) | ||
| """The middleware class to use. | ||
|
|
||
| Should be a subclass of OpenTelemetry | ||
| InstrumentationMiddleware][litestar.plugins.opentelemetry.OpenTelemetryInstrumentationMiddleware]. | ||
| """ | ||
|
|
||
| @property | ||
| def middleware(self) -> DefineMiddleware: | ||
| """Create an instance of :class:`DefineMiddleware <litestar.middleware.base.DefineMiddleware>` that wraps with. | ||
|
|
||
| [OpenTelemetry | ||
| InstrumentationMiddleware][litestar.plugins.opentelemetry.OpenTelemetryInstrumentationMiddleware] or a subclass | ||
| of this middleware. | ||
|
|
||
| Returns: | ||
| An instance of ``DefineMiddleware``. | ||
| """ | ||
| return DefineMiddleware(self.middleware_class, config=self) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,58 @@ | ||
| from __future__ import annotations | ||
|
|
||
| from typing import TYPE_CHECKING | ||
|
|
||
| from litestar.exceptions import MissingDependencyException | ||
| from litestar.middleware.base import AbstractMiddleware | ||
|
|
||
| __all__ = ("OpenTelemetryInstrumentationMiddleware",) | ||
|
|
||
|
|
||
| try: | ||
| import opentelemetry # noqa: F401 | ||
| except ImportError as e: | ||
| raise MissingDependencyException("opentelemetry") from e | ||
|
|
||
| from opentelemetry.instrumentation.asgi import OpenTelemetryMiddleware | ||
| from opentelemetry.util.http import get_excluded_urls | ||
|
|
||
| if TYPE_CHECKING: | ||
| from litestar.plugins.opentelemetry import OpenTelemetryConfig | ||
| from litestar.types import ASGIApp, Receive, Scope, Send | ||
|
|
||
|
|
||
| class OpenTelemetryInstrumentationMiddleware(AbstractMiddleware): | ||
| """OpenTelemetry Middleware.""" | ||
|
|
||
| def __init__(self, app: ASGIApp, config: OpenTelemetryConfig) -> None: | ||
| """Middleware that adds OpenTelemetry instrumentation to the application. | ||
|
|
||
| Args: | ||
| app: The ``next`` ASGI app to call. | ||
| config: An instance of :class:`OpenTelemetryConfig <.plugins.opentelemetry.OpenTelemetryConfig>` | ||
| """ | ||
| super().__init__(app=app, scopes=config.scopes, exclude=config.exclude, exclude_opt_key=config.exclude_opt_key) | ||
| self.open_telemetry_middleware = OpenTelemetryMiddleware( | ||
| app=app, | ||
| client_request_hook=config.client_request_hook_handler, # type: ignore[arg-type] | ||
| client_response_hook=config.client_response_hook_handler, # type: ignore[arg-type] | ||
| default_span_details=config.scope_span_details_extractor, | ||
| excluded_urls=get_excluded_urls(config.exclude_urls_env_key), | ||
| meter=config.meter, | ||
| meter_provider=config.meter_provider, | ||
| server_request_hook=config.server_request_hook_handler, | ||
| tracer_provider=config.tracer_provider, | ||
| ) | ||
|
|
||
| async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: | ||
| """ASGI callable. | ||
|
|
||
| Args: | ||
| scope: The ASGI connection scope. | ||
| receive: The ASGI receive function. | ||
| send: The ASGI send function. | ||
|
|
||
| Returns: | ||
| None | ||
| """ | ||
| await self.open_telemetry_middleware(scope, receive, send) # type: ignore[arg-type] # pyright: ignore[reportGeneralTypeIssues] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| from __future__ import annotations | ||
|
|
||
| from typing import TYPE_CHECKING | ||
|
|
||
| from litestar.middleware.base import DefineMiddleware | ||
| from litestar.plugins import InitPlugin | ||
| from litestar.plugins.opentelemetry.config import OpenTelemetryConfig | ||
| from litestar.plugins.opentelemetry.middleware import OpenTelemetryInstrumentationMiddleware | ||
|
|
||
| if TYPE_CHECKING: | ||
| from litestar.config.app import AppConfig | ||
| from litestar.types.composite_types import Middleware | ||
|
|
||
|
|
||
| class OpenTelemetryPlugin(InitPlugin): | ||
| """OpenTelemetry Plugin.""" | ||
|
|
||
| __slots__ = ("_middleware", "config") | ||
|
|
||
| def __init__(self, config: OpenTelemetryConfig | None = None) -> None: | ||
| self.config = config or OpenTelemetryConfig() | ||
| self._middleware: DefineMiddleware | None = None | ||
| super().__init__() | ||
|
|
||
| @property | ||
| def middleware(self) -> DefineMiddleware: | ||
| if self._middleware: | ||
| return self._middleware | ||
| return DefineMiddleware(OpenTelemetryInstrumentationMiddleware, config=self.config) | ||
|
|
||
| def on_app_init(self, app_config: AppConfig) -> AppConfig: | ||
| app_config.middleware, _middleware = self._pop_otel_middleware(app_config.middleware) | ||
| return app_config | ||
|
|
||
| @staticmethod | ||
| def _pop_otel_middleware(middlewares: list[Middleware]) -> tuple[list[Middleware], DefineMiddleware | None]: | ||
| """Get the OpenTelemetry middleware if it is enabled in the application. | ||
| Remove the middleware from the list of middlewares if it is found. | ||
| """ | ||
| otel_middleware: DefineMiddleware | None = None | ||
| other_middlewares = [] | ||
| for middleware in middlewares: | ||
| if ( | ||
| isinstance(middleware, DefineMiddleware) | ||
| and isinstance(middleware.middleware, type) | ||
| and issubclass(middleware.middleware, OpenTelemetryInstrumentationMiddleware) | ||
| ): | ||
| otel_middleware = middleware | ||
| else: | ||
| other_middlewares.append(middleware) | ||
| return other_middlewares, otel_middleware |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For a deprecation that's scheduled for a removal, this should be pointed at the v2 branch :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have some followup changes for this in the main branch that i was making in a followup PR. Can I open another PR once this merges to get this into the v2 branch?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess you could merged these into
mainand then cherry-pick them intov2