File size: 5,042 Bytes
9c6594c |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
from contextlib import contextmanager
import sentry_sdk
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.utils import (
capture_internal_exceptions,
ensure_integration_enabled,
event_from_exception,
package_version,
)
try:
from graphene.types import schema as graphene_schema # type: ignore
except ImportError:
raise DidNotEnable("graphene is not installed")
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from collections.abc import Generator
from typing import Any, Dict, Union
from graphene.language.source import Source # type: ignore
from graphql.execution import ExecutionResult
from graphql.type import GraphQLSchema
from sentry_sdk._types import Event
class GrapheneIntegration(Integration):
identifier = "graphene"
@staticmethod
def setup_once():
# type: () -> None
version = package_version("graphene")
_check_minimum_version(GrapheneIntegration, version)
_patch_graphql()
def _patch_graphql():
# type: () -> None
old_graphql_sync = graphene_schema.graphql_sync
old_graphql_async = graphene_schema.graphql
@ensure_integration_enabled(GrapheneIntegration, old_graphql_sync)
def _sentry_patched_graphql_sync(schema, source, *args, **kwargs):
# type: (GraphQLSchema, Union[str, Source], Any, Any) -> ExecutionResult
scope = sentry_sdk.get_isolation_scope()
scope.add_event_processor(_event_processor)
with graphql_span(schema, source, kwargs):
result = old_graphql_sync(schema, source, *args, **kwargs)
with capture_internal_exceptions():
client = sentry_sdk.get_client()
for error in result.errors or []:
event, hint = event_from_exception(
error,
client_options=client.options,
mechanism={
"type": GrapheneIntegration.identifier,
"handled": False,
},
)
sentry_sdk.capture_event(event, hint=hint)
return result
async def _sentry_patched_graphql_async(schema, source, *args, **kwargs):
# type: (GraphQLSchema, Union[str, Source], Any, Any) -> ExecutionResult
integration = sentry_sdk.get_client().get_integration(GrapheneIntegration)
if integration is None:
return await old_graphql_async(schema, source, *args, **kwargs)
scope = sentry_sdk.get_isolation_scope()
scope.add_event_processor(_event_processor)
with graphql_span(schema, source, kwargs):
result = await old_graphql_async(schema, source, *args, **kwargs)
with capture_internal_exceptions():
client = sentry_sdk.get_client()
for error in result.errors or []:
event, hint = event_from_exception(
error,
client_options=client.options,
mechanism={
"type": GrapheneIntegration.identifier,
"handled": False,
},
)
sentry_sdk.capture_event(event, hint=hint)
return result
graphene_schema.graphql_sync = _sentry_patched_graphql_sync
graphene_schema.graphql = _sentry_patched_graphql_async
def _event_processor(event, hint):
# type: (Event, Dict[str, Any]) -> Event
if should_send_default_pii():
request_info = event.setdefault("request", {})
request_info["api_target"] = "graphql"
elif event.get("request", {}).get("data"):
del event["request"]["data"]
return event
@contextmanager
def graphql_span(schema, source, kwargs):
# type: (GraphQLSchema, Union[str, Source], Dict[str, Any]) -> Generator[None, None, None]
operation_name = kwargs.get("operation_name")
operation_type = "query"
op = OP.GRAPHQL_QUERY
if source.strip().startswith("mutation"):
operation_type = "mutation"
op = OP.GRAPHQL_MUTATION
elif source.strip().startswith("subscription"):
operation_type = "subscription"
op = OP.GRAPHQL_SUBSCRIPTION
sentry_sdk.add_breadcrumb(
crumb={
"data": {
"operation_name": operation_name,
"operation_type": operation_type,
},
"category": "graphql.operation",
},
)
scope = sentry_sdk.get_current_scope()
if scope.span:
_graphql_span = scope.span.start_child(op=op, name=operation_name)
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)
try:
yield
finally:
_graphql_span.finish()
|