Skip to content
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

fix(asgi): set http status when exception raised #1645

Merged
merged 12 commits into from
Sep 29, 2020
Merged

Conversation

majorgreys
Copy link
Collaborator

@majorgreys majorgreys commented Sep 4, 2020

Description

When exception is raised by app call, the span for the ASGI request should have an HTTP status code set. This can be customized by a user of the library to update spans based on the application's exceptions.

Checklist

  • Entry added to CHANGELOG.md.
  • Tests provided; and/or
  • Description of manual testing performed and explanation is included in the code and/or PR.
  • Library documentation is updated.
  • Corp site documentation is updated (link to the PR).

@majorgreys majorgreys requested a review from a team as a code owner September 4, 2020 21:39
@@ -123,6 +123,7 @@ async def wrapped_send(message):
except Exception:
(exc_type, exc_val, exc_tb) = sys.exc_info()
span.set_exc_info(exc_type, exc_val, exc_tb)
span.set_tag(http.STATUS_CODE, 500)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the response code guaranteed to be 500 by the server if an error occurs? Just want to be sure that we're not going set a 500 status code when another response code could be returned.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have exactly this problem I had to intern the middleware.py and edit it to remove the span.finish() from the finally :

try:
    await self.app(scope, receive, send_with_tracing)
except BaseException as exc:
    span.set_traceback()
    raise exc from None
span.finish()

Since I have a Wrapper managing the status to raise my custom error code ( 500 or 503 ... )

from .middleware_ddtrace_asgi import TraceMiddleware
from ddtrace.ext import http as http_tags
from starlette.responses import Response
from starlette.types import Receive, Scope, Send
from fastapi.requests import Request

class ExceptionTraceMiddleware(TraceMiddleware):

    def __init__(self, **kwds):
        super().__init__(**kwds)

    async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
        if scope["type"] != "http":
            return await self.app(scope, receive, send)

        request = Request(scope=scope, receive=receive)
        try:
            await super(ExceptionTraceMiddleware, self).__call__(scope, receive, send)
        except errors.ApiError as exc:  # only our case
            response: Response = handle_api_error(request, exc)
            span = self.tracer.current_span()
            if span:
                span.set_tag(http_tags.STATUS_CODE, response.status_code)
                span.finish()
            await response.__call__(scope, receive, send)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@raphaelauv By the looks of this code, I am guessing you are using the ddtrace-asgi library. But I think your solution is in line with what I have contributed here since the tag has to be set before the span is finished.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Off course I'm using ddtrace-asgi :

I had to intern the middleware.py and edit ... !

and take a look to the definition of my custom class , it's extending TraceMiddleware.

And No your solution is not equivalent because your putting 500 in all case and do not provide any mechanism to custom that value in case of error/exception.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for highlighting this. I've added a static method on the middleware that can be overridden by a subclass to allow custom handling of any exceptions raised by the application.

https://github.com/DataDog/dd-trace-py/pull/1645/files#diff-61d5c837e13077f7c9d719d4cd911a59R79-R82

Hopefully that addresses your concerns.

@Kyle-Verhoog Kyle-Verhoog added this to the 0.43.0 milestone Sep 23, 2020
Kyle-Verhoog
Kyle-Verhoog previously approved these changes Sep 23, 2020
@@ -77,6 +77,11 @@ class TraceMiddleware:
tracer: Custom tracer. Defaults to the global tracer.
"""

@staticmethod
def handle_exception_span(exc, span):
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe people would like to use the request that generated the error.

It's the default behavior in fastAPI for a custom error handler

something like :

def handle_exception_span(request, exc, span): 

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this generic ASGI middleware, we do not have access to a request/response interface as you would find in something like with a Starlette middleware's dispatch method.

We are working now on support for a Starlette middleware, where we could certainly provide library users a way to accomplish this.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay it make sense , thank you for your answer.

Copy link
Member

@Kyle-Verhoog Kyle-Verhoog left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO I'd prefer to pass a function to the init of the middleware rather than having to subclass TraceMiddleware. Especially since the method is static, there's really no reason to have it on the class. Otherwise looks good to me!

@majorgreys majorgreys merged commit 140fdb2 into master Sep 29, 2020
@majorgreys majorgreys deleted the majorgreys/asgi500 branch September 29, 2020 11:54
Kyle-Verhoog pushed a commit that referenced this pull request Oct 1, 2020
* fix(asgi): set http status when exception raised

* add fix

* support custom exception handling

* use function for custom exception

* update changelog
Kyle-Verhoog pushed a commit that referenced this pull request Oct 1, 2020
* fix(asgi): set http status when exception raised

* add fix

* support custom exception handling

* use function for custom exception

* update changelog
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants