Skip to content

Commit 50120ce

Browse files
committed
rewrite FastAPIInstrumentor:build_middleware_stack to become failsafe
1 parent 9bf77b5 commit 50120ce

File tree

1 file changed

+20
-26
lines changed
  • instrumentation/opentelemetry-instrumentation-fastapi/src/opentelemetry/instrumentation/fastapi

1 file changed

+20
-26
lines changed

instrumentation/opentelemetry-instrumentation-fastapi/src/opentelemetry/instrumentation/fastapi/__init__.py

Lines changed: 20 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -289,22 +289,33 @@ def instrument_app(
289289
schema_url=_get_schema_url(sem_conv_opt_in_mode),
290290
)
291291

292-
# Instead of using `app.add_middleware` we monkey patch `build_middleware_stack` to insert our middleware
293-
# as the outermost middleware.
294-
# Otherwise `OpenTelemetryMiddleware` would have unhandled exceptions tearing through it and would not be able
295-
# to faithfully record what is returned to the client since it technically cannot know what `ServerErrorMiddleware` is going to do.
296-
292+
# In order to make traces available at any stage of the request
293+
# processing - including exception handling - we wrap ourselves as
294+
# the new, outermost middleware. However in order to prevent
295+
# exceptions from user-provided hooks of tearing through, we wrap
296+
# them to return without failure unconditionally.
297297
def build_middleware_stack(self: Starlette) -> ASGIApp:
298+
def failsafe(func):
299+
@functools.wraps(func)
300+
def wrapper(*args, **kwargs):
301+
try:
302+
return func(*args, **kwargs)
303+
except Exception:
304+
pass
305+
306+
return wrapper
307+
298308
inner_server_error_middleware: ASGIApp = ( # type: ignore
299309
self._original_build_middleware_stack() # type: ignore
300310
)
301-
otel_middleware = OpenTelemetryMiddleware(
311+
312+
return OpenTelemetryMiddleware(
302313
inner_server_error_middleware,
303314
excluded_urls=excluded_urls,
304315
default_span_details=_get_default_span_details,
305-
server_request_hook=server_request_hook,
306-
client_request_hook=client_request_hook,
307-
client_response_hook=client_response_hook,
316+
server_request_hook=failsafe(server_request_hook),
317+
client_request_hook=failsafe(client_request_hook),
318+
client_response_hook=failsafe(client_response_hook),
308319
# Pass in tracer/meter to get __name__and __version__ of fastapi instrumentation
309320
tracer=tracer,
310321
meter=meter,
@@ -313,23 +324,6 @@ def build_middleware_stack(self: Starlette) -> ASGIApp:
313324
http_capture_headers_sanitize_fields=http_capture_headers_sanitize_fields,
314325
exclude_spans=exclude_spans,
315326
)
316-
# Wrap in an outer layer of ServerErrorMiddleware so that any exceptions raised in OpenTelemetryMiddleware
317-
# are handled.
318-
# This should not happen unless there is a bug in OpenTelemetryMiddleware, but if there is we don't want that
319-
# to impact the user's application just because we wrapped the middlewares in this order.
320-
if isinstance(
321-
inner_server_error_middleware, ServerErrorMiddleware
322-
): # usually true
323-
outer_server_error_middleware = ServerErrorMiddleware(
324-
app=otel_middleware,
325-
)
326-
else:
327-
# Something else seems to have patched things, or maybe Starlette changed.
328-
# Just create a default ServerErrorMiddleware.
329-
outer_server_error_middleware = ServerErrorMiddleware(
330-
app=otel_middleware
331-
)
332-
return outer_server_error_middleware
333327

334328
app._original_build_middleware_stack = app.build_middleware_stack
335329
app.build_middleware_stack = types.MethodType(

0 commit comments

Comments
 (0)