from functools import wraps from starlette.types import ASGIApp, Message, Receive, Scope, Send from .ctx import TraceCtx from .span import Span, get_current_span class TraceASGIMiddleware: """ fastapi-example: app = FastAPI() app.add_middleware(TraceASGIMiddleware) """ def __init__(self, app: ASGIApp) -> None: self.app = app @staticmethod async def my_receive(receive: Receive, span: Span) -> Receive: await span.request_before() @wraps(receive) async def my_receive() -> Message: message = await receive() await span.request_after(message) return message return my_receive async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: if scope['type'] != 'http': await self.app(scope, receive, send) return async with get_current_span(scope) as span: handle_outgoing_receive = await self.my_receive(receive, span) async def handle_outgoing_request(message: 'Message') -> None: await span.response(message) await send(message) try: await self.app(scope, handle_outgoing_receive, handle_outgoing_request) finally: TraceCtx.clear()