Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 25 additions & 6 deletions sentry_sdk/integrations/graphene.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
from sentry_sdk.consts import OP
from sentry_sdk.integrations import _check_minimum_version, DidNotEnable, Integration
from sentry_sdk.scope import should_send_default_pii
from sentry_sdk.traces import StreamedSpan
from sentry_sdk.tracing_utils import has_span_streaming_enabled
from sentry_sdk.utils import (
capture_internal_exceptions,
ensure_integration_enabled,
Expand Down Expand Up @@ -120,6 +122,8 @@
def graphql_span(
schema: "GraphQLSchema", source: "Union[str, Source]", kwargs: "Dict[str, Any]"
) -> "Generator[None, None, None]":
client = sentry_sdk.get_client()

operation_name = kwargs.get("operation_name")

operation_type = "query"
Expand All @@ -141,15 +145,30 @@
},
)

_graphql_span = sentry_sdk.start_span(op=op, name=operation_name)
if has_span_streaming_enabled(client.options):
attributes = {
"graphql.document": source,
"graphql.operation.name": operation_name,
"graphql.operation.type": operation_type,
"sentry.op": op,
}
_graphql_span = sentry_sdk.traces.start_span(
name=operation_name, attributes=attributes
)

else:
_graphql_span = sentry_sdk.start_span(op=op, name=operation_name)

_graphql_span.set_data("graphql.document", source)
_graphql_span.set_data("graphql.operation.name", operation_name)
_graphql_span.set_data("graphql.operation.type", operation_type)
_graphql_span.set_data("graphql.document", source)
_graphql_span.set_data("graphql.operation.name", operation_name)
_graphql_span.set_data("graphql.operation.type", operation_type)

_graphql_span.__enter__()
_graphql_span.__enter__()

try:
yield
finally:
_graphql_span.__exit__(None, None, None)
if isinstance(_graphql_span, StreamedSpan):
_graphql_span.end()
else:
_graphql_span.__exit__(None, None, None)

Check warning on line 174 in sentry_sdk/integrations/graphene.py

View check run for this annotation

@sentry/warden / warden: code-review

StreamedSpan exceptions not marked as errors due to bypassing __exit__

When `has_span_streaming_enabled` is True, the span is created but never entered as a context manager (no `__enter__` call), and ended directly via `.end()` instead of `__exit__`. This skips the exception handling logic in `StreamedSpan.__exit__` that sets `status = SpanStatus.ERROR` when an exception occurs. If a GraphQL operation throws an exception, the streaming span will be marked as 'ok' instead of 'error', losing important error information.
Comment on lines 168 to +174
Copy link

Choose a reason for hiding this comment

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

StreamedSpan exceptions not marked as errors due to bypassing exit

When has_span_streaming_enabled is True, the span is created but never entered as a context manager (no __enter__ call), and ended directly via .end() instead of __exit__. This skips the exception handling logic in StreamedSpan.__exit__ that sets status = SpanStatus.ERROR when an exception occurs. If a GraphQL operation throws an exception, the streaming span will be marked as 'ok' instead of 'error', losing important error information.

Verification

Read traces.py:310-323 confirming exit handles exception status via should_be_treated_as_error, while end() (lines 325-332) just calls _end() without exception handling. Read asgi.py:254,290 showing that ASGI uses streaming spans correctly via with span_ctx as span: context manager pattern.

Identified by Warden code-review · G3U-L27

Loading