Skip to content

feat(asgi): Make integration fully span first compatible#5920

Open
sentrivana wants to merge 25 commits intomasterfrom
ivana/migrate-asgi-event-processor
Open

feat(asgi): Make integration fully span first compatible#5920
sentrivana wants to merge 25 commits intomasterfrom
ivana/migrate-asgi-event-processor

Conversation

@sentrivana
Copy link
Copy Markdown
Contributor

@sentrivana sentrivana commented Mar 30, 2026

Description

In span first, there are no event processors. Therefore, we need to be able to set the data we were setting in event processors differently.

As we migrate our integrations one by one, this will be an exercise in whether it's possible to achieve this without some sort of callback/lifecycle hooks. So far, in ASGI, it seems we can get by by simply using scope.set_attribute() for setting request related data, and updating the segment name/source just before the span ends.

Adding this enables us to actually test the new functionality.

To sum up, this PR:

  • makes the ASGI integration fully span-first-compatible: data set on spans currently should appear as attributes in span first
  • introduces a test helper that makes it easier to unpack envelope items (useful especially when a test checks both old- and new-style telemetry)
  • fully tests the ASGI span streaming implementation

Like in other span first PRs, there is quite a bit of intentional code duplication so that it's easier to remove the legacy implementation in the next major.

Issues

Reminders

@linear-code
Copy link
Copy Markdown

linear-code bot commented Mar 30, 2026

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 30, 2026

Semver Impact of This PR

🟡 Minor (new features)

📋 Changelog Preview

This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).


This PR will not appear in the changelog.


🤖 This preview updates automatically when you update the PR.

1 similar comment
@github-actions
Copy link
Copy Markdown
Contributor

Semver Impact of This PR

🟡 Minor (new features)

📋 Changelog Preview

This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).


This PR will not appear in the changelog.


🤖 This preview updates automatically when you update the PR.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 30, 2026

Codecov Results 📊

142 passed | Total: 142 | Pass Rate: 100% | Execution Time: 21.70s

All tests are passing successfully.

❌ Patch coverage is 7.41%. Project has 14206 uncovered lines.

Files with missing lines (4)
File Patch % Lines
traces.py 35.71% ⚠️ 198 Missing
asgi.py 15.61% ⚠️ 173 Missing
_wsgi_common.py 28.24% ⚠️ 94 Missing
_asgi_common.py 14.08% ⚠️ 61 Missing

Generated by Codecov Action

return request_data


def _get_request_attributes(asgi_scope: "Any") -> "dict[str, Any]":
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is an attributes based copy of _get_request_data just above


return name, source

def _get_segment_name_and_source(
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is a copy of _get_transaction_name_and_source above, just adapted for segments

@sentrivana sentrivana marked this pull request as ready for review April 13, 2026 07:52
@sentrivana sentrivana requested a review from a team as a code owner April 13, 2026 07:52
@sentrivana sentrivana changed the title feat(asgi): Migrate away from event processor in span first feat(asgi): Make integration fully span first compatible Apr 13, 2026
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

Bugbot Autofix prepared fixes for both issues found in the latest run.

  • ✅ Fixed: Redundant None check after isinstance guard
    • Removed the redundant span is not None condition inside the isinstance(span, StreamedSpan) block to simplify dead logic.
  • ✅ Fixed: Duplicate function only differs by enum type used
    • Refactored _get_segment_name_and_source to delegate to _get_transaction_name_and_source and convert the shared enum result to a string, eliminating duplicated logic.

Create PR

Or push these changes by commenting:

@cursor push d24d2ecc4c
Preview (d24d2ecc4c)
diff --git a/sentry_sdk/integrations/asgi.py b/sentry_sdk/integrations/asgi.py
--- a/sentry_sdk/integrations/asgi.py
+++ b/sentry_sdk/integrations/asgi.py
@@ -27,7 +27,6 @@
 from sentry_sdk.traces import (
     StreamedSpan,
     SegmentSource,
-    SOURCE_FOR_STYLE as SEGMENT_SOURCE_FOR_STYLE,
 )
 from sentry_sdk.tracing import (
     SOURCE_FOR_STYLE,
@@ -45,7 +44,6 @@
     _get_installed_modules,
     reraise,
     capture_internal_exceptions,
-    qualname_from_function,
 )
 
 from typing import TYPE_CHECKING
@@ -364,8 +362,7 @@
                         finally:
                             if isinstance(span, StreamedSpan):
                                 already_set = (
-                                    span is not None
-                                    and span.name != _DEFAULT_TRANSACTION_NAME
+                                    span.name != _DEFAULT_TRANSACTION_NAME
                                     and span.get_attributes().get("sentry.span.source")
                                     in [
                                         SegmentSource.COMPONENT.value,
@@ -460,36 +457,5 @@
     def _get_segment_name_and_source(
         self: "SentryAsgiMiddleware", segment_style: str, asgi_scope: "Any"
     ) -> "Tuple[str, str]":
-        name = None
-        source = SEGMENT_SOURCE_FOR_STYLE[segment_style].value
-        ty = asgi_scope.get("type")
-
-        if segment_style == "endpoint":
-            endpoint = asgi_scope.get("endpoint")
-            # Webframeworks like Starlette mutate the ASGI env once routing is
-            # done, which is sometime after the request has started. If we have
-            # an endpoint, overwrite our generic transaction name.
-            if endpoint:
-                name = qualname_from_function(endpoint) or ""
-            else:
-                name = _get_url(asgi_scope, "http" if ty == "http" else "ws", host=None)
-                source = SegmentSource.URL.value
-
-        elif segment_style == "url":
-            # FastAPI includes the route object in the scope to let Sentry extract the
-            # path from it for the transaction name
-            route = asgi_scope.get("route")
-            if route:
-                path = getattr(route, "path", None)
-                if path is not None:
-                    name = path
-            else:
-                name = _get_url(asgi_scope, "http" if ty == "http" else "ws", host=None)
-                source = SegmentSource.URL.value
-
-        if name is None:
-            name = _DEFAULT_TRANSACTION_NAME
-            source = SegmentSource.ROUTE.value
-            return name, source
-
-        return name, source
+        name, source = self._get_transaction_name_and_source(segment_style, asgi_scope)
+        return name, str(source)

This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 77b9298. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Event processor replacement for streamed spans

1 participant