getsentry / sentry-python

The official Python SDK for Sentry.io
https://sentry.io/for/python/
MIT License
1.9k stars 503 forks source link

Django StreamingHttpResponse View not working as expected #3736

Open marcoaleixo opened 2 days ago

marcoaleixo commented 2 days ago

How do you use Sentry?

Sentry Saas (sentry.io)

Version

2.18.0

Steps to Reproduce

  1. Have a view like
class StreamingView(APIView):

    def post(self, request, *args, **kwargs):

        span = sentry_sdk.start_span(name=f"Log: {record.msg}", op="log.info", sampled=False)
        print(span)
        span.set_data("value", record.msg)
        span.set_data("filename", f"{record.pathname} at line {record.lineno}")
        span.finish()

        generator_streaming = dummy_function.execute(parameters=parameters)

        return StreamingHttpResponse(generator_streaming, content_type='text/event-stream')

Here the span.trace_id will be for instance "1234"

  1. Inside the dummy_function
    def execute():
        span = sentry_sdk.start_span(name=f"Log: {record.msg}", op="log.info", sampled=False)
        print(span)
        span.set_data("value", record.msg)
        span.set_data("filename", f"{record.pathname} at line {record.lineno}")
        span.finish()

In this function, the trace_id will be totally different.

I think that this is related the StreamingHttpResponse, because the Sentry is working fine for all my other views.

Feel free to ask more details to help in the investigation.

FROM python:3.9.18-slim-bullseye Django==4.1 djangorestframework==3.14

I'm executing my server with python manage.py runserver 0.0.0.0:8000 In production we use something like gunicorn api.wsgi -b 0.0.0.0:8081

Expected Result

The trace_id should stay the same in StreamingHttpResponse views and underlying function calls.

Actual Result

The trace_id is different between the view and underlying function calls.

marcoaleixo commented 2 days ago

Do we have a way to set the trace_id? Maybe an easy fix is to get the trace_id in the view level and pass as parameter to my function and do a transaction.set_trace_id(trace_id=trace_id).

But it seems that we don't have a set_trace_id thing at transaction/event level, right?

I know that we can do a Span(trace_id=myid), but passing this to ALL calls downstream is bad. 😅

antonpirker commented 16 hours ago

Hey @marcoaleixo thanks for raising this, I will have a look!

antonpirker commented 15 hours ago

Indeed!

I have now reproduced this with this demo application: https://github.com/antonpirker/testing-sentry/blob/main/test-django-rest-framework-streaming/mysite/api/views.py

The first span "Streaming View" is present in the trace, but the second span "Stream Response Generator" is missing (because it has the wrong trace id) Image

antonpirker commented 15 hours ago

This is a bug. I guess it has something to do with how Django handles streaming views and what we monkey patch in Django. Sentry is probably not patching the functions in Django that are handling the streaming responses.