-
Notifications
You must be signed in to change notification settings - Fork 464
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(fastapi): add FastAPI Integration (#1495)
Add FastAPI integration that sets transaction names and sources (the rest is done in the Starlette integration)
- Loading branch information
1 parent
698c24b
commit 8f4db31
Showing
10 changed files
with
359 additions
and
98 deletions.
There are no files selected for viewing
This file contains 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 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 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,122 @@ | ||
from sentry_sdk._types import MYPY | ||
from sentry_sdk.hub import Hub | ||
from sentry_sdk.integrations import DidNotEnable | ||
from sentry_sdk.integrations.starlette import ( | ||
SentryStarletteMiddleware, | ||
StarletteIntegration, | ||
) | ||
from sentry_sdk.tracing import SOURCE_FOR_STYLE, TRANSACTION_SOURCE_ROUTE | ||
from sentry_sdk.utils import transaction_from_function | ||
|
||
if MYPY: | ||
from typing import Any, Callable, Dict | ||
|
||
from sentry_sdk._types import Event | ||
|
||
try: | ||
from fastapi.applications import FastAPI | ||
from fastapi.requests import Request | ||
except ImportError: | ||
raise DidNotEnable("FastAPI is not installed") | ||
|
||
try: | ||
from starlette.types import ASGIApp, Receive, Scope, Send | ||
except ImportError: | ||
raise DidNotEnable("Starlette is not installed") | ||
|
||
|
||
_DEFAULT_TRANSACTION_NAME = "generic FastApi request" | ||
|
||
|
||
class FastApiIntegration(StarletteIntegration): | ||
identifier = "fastapi" | ||
|
||
@staticmethod | ||
def setup_once(): | ||
# type: () -> None | ||
StarletteIntegration.setup_once() | ||
patch_middlewares() | ||
|
||
|
||
def patch_middlewares(): | ||
# type: () -> None | ||
|
||
old_build_middleware_stack = FastAPI.build_middleware_stack | ||
|
||
def _sentry_build_middleware_stack(self): | ||
# type: (FastAPI) -> Callable[..., Any] | ||
""" | ||
Adds `SentryStarletteMiddleware` and `SentryFastApiMiddleware` to the | ||
middleware stack of the FastAPI application. | ||
""" | ||
app = old_build_middleware_stack(self) | ||
app = SentryStarletteMiddleware(app=app) | ||
app = SentryFastApiMiddleware(app=app) | ||
return app | ||
|
||
FastAPI.build_middleware_stack = _sentry_build_middleware_stack | ||
|
||
|
||
def _set_transaction_name_and_source(event, transaction_style, request): | ||
# type: (Event, str, Any) -> None | ||
name = "" | ||
|
||
if transaction_style == "endpoint": | ||
endpoint = request.scope.get("endpoint") | ||
if endpoint: | ||
name = transaction_from_function(endpoint) or "" | ||
|
||
elif transaction_style == "url": | ||
route = request.scope.get("route") | ||
if route: | ||
path = getattr(route, "path", None) | ||
if path is not None: | ||
name = path | ||
|
||
if not name: | ||
event["transaction"] = _DEFAULT_TRANSACTION_NAME | ||
event["transaction_info"] = {"source": TRANSACTION_SOURCE_ROUTE} | ||
return | ||
|
||
event["transaction"] = name | ||
event["transaction_info"] = {"source": SOURCE_FOR_STYLE[transaction_style]} | ||
|
||
|
||
class SentryFastApiMiddleware: | ||
def __init__(self, app, dispatch=None): | ||
# type: (ASGIApp, Any) -> None | ||
self.app = app | ||
|
||
async def __call__(self, scope, receive, send): | ||
# type: (Scope, Receive, Send) -> Any | ||
if scope["type"] != "http": | ||
await self.app(scope, receive, send) | ||
return | ||
|
||
hub = Hub.current | ||
integration = hub.get_integration(FastApiIntegration) | ||
if integration is None: | ||
return | ||
|
||
with hub.configure_scope() as sentry_scope: | ||
request = Request(scope, receive=receive, send=send) | ||
|
||
def _make_request_event_processor(req, integration): | ||
# type: (Any, Any) -> Callable[[Dict[str, Any], Dict[str, Any]], Dict[str, Any]] | ||
def event_processor(event, hint): | ||
# type: (Dict[str, Any], Dict[str, Any]) -> Dict[str, Any] | ||
|
||
_set_transaction_name_and_source( | ||
event, integration.transaction_style, req | ||
) | ||
|
||
return event | ||
|
||
return event_processor | ||
|
||
sentry_scope._name = FastApiIntegration.identifier | ||
sentry_scope.add_event_processor( | ||
_make_request_event_processor(request, integration) | ||
) | ||
|
||
await self.app(scope, receive, send) |
This file contains 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 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 was deleted.
Oops, something went wrong.
This file contains 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,3 @@ | ||
import pytest | ||
|
||
pytest.importorskip("fastapi") |
Oops, something went wrong.