1
+ from contextlib import contextmanager
2
+
1
3
import sentry_sdk
4
+ from sentry_sdk .consts import OP
2
5
from sentry_sdk .integrations import DidNotEnable , Integration
3
6
from sentry_sdk .scope import Scope , should_send_default_pii
4
7
from sentry_sdk .utils import (
17
20
18
21
19
22
if TYPE_CHECKING :
23
+ from collections .abc import Generator
20
24
from typing import Any , Dict , Union
21
25
from graphene .language .source import Source # type: ignore
22
26
from graphql .execution import ExecutionResult # type: ignore
@@ -52,13 +56,15 @@ def _sentry_patched_graphql_sync(schema, source, *args, **kwargs):
52
56
scope = Scope .get_isolation_scope ()
53
57
scope .add_event_processor (_event_processor )
54
58
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 )
56
61
57
62
with capture_internal_exceptions ():
63
+ client = sentry_sdk .get_client ()
58
64
for error in result .errors or []:
59
65
event , hint = event_from_exception (
60
66
error ,
61
- client_options = sentry_sdk . get_client () .options ,
67
+ client_options = client .options ,
62
68
mechanism = {
63
69
"type" : GrapheneIntegration .identifier ,
64
70
"handled" : False ,
@@ -70,19 +76,22 @@ def _sentry_patched_graphql_sync(schema, source, *args, **kwargs):
70
76
71
77
async def _sentry_patched_graphql_async (schema , source , * args , ** kwargs ):
72
78
# 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 :
74
81
return await old_graphql_async (schema , source , * args , ** kwargs )
75
82
76
83
scope = Scope .get_isolation_scope ()
77
84
scope .add_event_processor (_event_processor )
78
85
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 )
80
88
81
89
with capture_internal_exceptions ():
90
+ client = sentry_sdk .get_client ()
82
91
for error in result .errors or []:
83
92
event , hint = event_from_exception (
84
93
error ,
85
- client_options = sentry_sdk . get_client () .options ,
94
+ client_options = client .options ,
86
95
mechanism = {
87
96
"type" : GrapheneIntegration .identifier ,
88
97
"handled" : False ,
@@ -106,3 +115,43 @@ def _event_processor(event, hint):
106
115
del event ["request" ]["data" ]
107
116
108
117
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