1+ from contextlib import contextmanager
2+
13import sentry_sdk
4+ from sentry_sdk .consts import OP
25from sentry_sdk .integrations import DidNotEnable , Integration
36from sentry_sdk .scope import Scope , should_send_default_pii
47from sentry_sdk .utils import (
1720
1821
1922if TYPE_CHECKING :
23+ from collections .abc import Generator
2024 from typing import Any , Dict , Union
2125 from graphene .language .source import Source # type: ignore
2226 from graphql .execution import ExecutionResult # type: ignore
@@ -52,13 +56,15 @@ def _sentry_patched_graphql_sync(schema, source, *args, **kwargs):
5256 scope = Scope .get_isolation_scope ()
5357 scope .add_event_processor (_event_processor )
5458
55- result = old_graphql_sync (schema , source , * args , ** kwargs )
59+ with graphql_span (schema , source , kwargs ):
60+ result = old_graphql_sync (schema , source , * args , ** kwargs )
5661
5762 with capture_internal_exceptions ():
63+ client = sentry_sdk .get_client ()
5864 for error in result .errors or []:
5965 event , hint = event_from_exception (
6066 error ,
61- client_options = sentry_sdk . get_client () .options ,
67+ client_options = client .options ,
6268 mechanism = {
6369 "type" : GrapheneIntegration .identifier ,
6470 "handled" : False ,
@@ -70,19 +76,22 @@ def _sentry_patched_graphql_sync(schema, source, *args, **kwargs):
7076
7177 async def _sentry_patched_graphql_async (schema , source , * args , ** kwargs ):
7278 # type: (GraphQLSchema, Union[str, Source], Any, Any) -> ExecutionResult
73- if sentry_sdk .get_client ().get_integration (GrapheneIntegration ) is None :
79+ integration = sentry_sdk .get_client ().get_integration (GrapheneIntegration )
80+ if integration is None :
7481 return await old_graphql_async (schema , source , * args , ** kwargs )
7582
7683 scope = Scope .get_isolation_scope ()
7784 scope .add_event_processor (_event_processor )
7885
79- result = await old_graphql_async (schema , source , * args , ** kwargs )
86+ with graphql_span (schema , source , kwargs ):
87+ result = await old_graphql_async (schema , source , * args , ** kwargs )
8088
8189 with capture_internal_exceptions ():
90+ client = sentry_sdk .get_client ()
8291 for error in result .errors or []:
8392 event , hint = event_from_exception (
8493 error ,
85- client_options = sentry_sdk . get_client () .options ,
94+ client_options = client .options ,
8695 mechanism = {
8796 "type" : GrapheneIntegration .identifier ,
8897 "handled" : False ,
@@ -106,3 +115,43 @@ def _event_processor(event, hint):
106115 del event ["request" ]["data" ]
107116
108117 return event
118+
119+
120+ @contextmanager
121+ def graphql_span (schema , source , kwargs ):
122+ # type: (GraphQLSchema, Union[str, Source], Dict[str, Any]) -> Generator[None, None, None]
123+ operation_name = kwargs .get ("operation_name" )
124+
125+ operation_type = "query"
126+ op = OP .GRAPHQL_QUERY
127+ if source .strip ().startswith ("mutation" ):
128+ operation_type = "mutation"
129+ op = OP .GRAPHQL_MUTATION
130+ elif source .strip ().startswith ("subscription" ):
131+ operation_type = "subscription"
132+ op = OP .GRAPHQL_SUBSCRIPTION
133+
134+ sentry_sdk .add_breadcrumb (
135+ crumb = {
136+ "data" : {
137+ "operation_name" : operation_name ,
138+ "operation_type" : operation_type ,
139+ },
140+ "category" : "graphql.operation" ,
141+ },
142+ )
143+
144+ scope = Scope .get_current_scope ()
145+ if scope .span :
146+ _graphql_span = scope .span .start_child (op = op , description = operation_name )
147+ else :
148+ _graphql_span = sentry_sdk .start_span (op = op , description = operation_name )
149+
150+ _graphql_span .set_data ("graphql.document" , source )
151+ _graphql_span .set_data ("graphql.operation.name" , operation_name )
152+ _graphql_span .set_data ("graphql.operation.type" , operation_type )
153+
154+ try :
155+ yield
156+ finally :
157+ _graphql_span .finish ()
0 commit comments