Browse Source

enhancement: DjangoDebugContext captures exceptions and allows captured stack traces to be queried (#1122)

pull/1149/head
Jason Kraus 1 year ago committed by GitHub
parent
commit
e9f25ecf2d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      docs/debug.rst
  2. 0
      graphene_django/debug/exception/__init__.py
  3. 17
      graphene_django/debug/exception/formating.py
  4. 10
      graphene_django/debug/exception/types.py
  5. 13
      graphene_django/debug/middleware.py
  6. 39
      graphene_django/debug/tests/test_query.py
  7. 4
      graphene_django/debug/types.py

6
docs/debug.rst

@ -4,7 +4,7 @@ Django Debug Middleware
You can debug your GraphQL queries in a similar way to
`django-debug-toolbar <https://django-debug-toolbar.readthedocs.org/>`__,
but outputting in the results in GraphQL response as fields, instead of
the graphical HTML interface.
the graphical HTML interface. Exceptions with their stack traces are also exposed.
For that, you will need to add the plugin in your graphene schema.
@ -63,6 +63,10 @@ the GraphQL request, like:
sql {
rawSql
}
exceptions {
message
stack
}
}
}

0
graphene_django/debug/exception/__init__.py

17
graphene_django/debug/exception/formating.py

@ -0,0 +1,17 @@
import traceback
from django.utils.encoding import force_str
from .types import DjangoDebugException
def wrap_exception(exception):
return DjangoDebugException(
message=force_str(exception),
exc_type=force_str(type(exception)),
stack="".join(
traceback.format_exception(
etype=type(exception), value=exception, tb=exception.__traceback__
)
),
)

10
graphene_django/debug/exception/types.py

@ -0,0 +1,10 @@
from graphene import ObjectType, String
class DjangoDebugException(ObjectType):
class Meta:
description = "Represents a single exception raised."
exc_type = String(required=True, description="The class of the exception")
message = String(required=True, description="The message of the exception")
stack = String(required=True, description="The stack trace")

13
graphene_django/debug/middleware.py

@ -3,6 +3,7 @@ from django.db import connections
from promise import Promise
from .sql.tracking import unwrap_cursor, wrap_cursor
from .exception.formating import wrap_exception
from .types import DjangoDebug
@ -10,8 +11,8 @@ class DjangoDebugContext(object):
def __init__(self):
self.debug_promise = None
self.promises = []
self.object = DjangoDebug(sql=[], exceptions=[])
self.enable_instrumentation()
self.object = DjangoDebug(sql=[])
def get_debug_promise(self):
if not self.debug_promise:
@ -19,6 +20,11 @@ class DjangoDebugContext(object):
self.promises = []
return self.debug_promise.then(self.on_resolve_all_promises).get()
def on_resolve_error(self, value):
if hasattr(self, "object"):
self.object.exceptions.append(wrap_exception(value))
return Promise.reject(value)
def on_resolve_all_promises(self, values):
if self.promises:
self.debug_promise = None
@ -57,6 +63,9 @@ class DjangoDebugMiddleware(object):
)
if info.schema.get_type("DjangoDebug") == info.return_type:
return context.django_debug.get_debug_promise()
promise = next(root, info, **args)
try:
promise = next(root, info, **args)
except Exception as e:
return context.django_debug.on_resolve_error(e)
context.django_debug.add_promise(promise)
return promise

39
graphene_django/debug/tests/test_query.py

@ -272,3 +272,42 @@ def test_should_query_connectionfilter(graphene_settings, max_limit):
assert "COUNT" in result.data["_debug"]["sql"][0]["rawSql"]
query = str(Reporter.objects.all()[:1].query)
assert result.data["_debug"]["sql"][1]["rawSql"] == query
def test_should_query_stack_trace():
class ReporterType(DjangoObjectType):
class Meta:
model = Reporter
interfaces = (Node,)
fields = "__all__"
class Query(graphene.ObjectType):
reporter = graphene.Field(ReporterType)
debug = graphene.Field(DjangoDebug, name="_debug")
def resolve_reporter(self, info, **args):
raise Exception("caught stack trace")
query = """
query ReporterQuery {
reporter {
lastName
}
_debug {
exceptions {
message
stack
}
}
}
"""
schema = graphene.Schema(query=Query)
result = schema.execute(
query, context_value=context(), middleware=[DjangoDebugMiddleware()]
)
assert result.errors
assert len(result.data["_debug"]["exceptions"])
debug_exception = result.data["_debug"]["exceptions"][0]
assert debug_exception["stack"].count("\n") > 1
assert "test_query.py" in debug_exception["stack"]
assert debug_exception["message"] == "caught stack trace"

4
graphene_django/debug/types.py

@ -1,6 +1,7 @@
from graphene import List, ObjectType
from .sql.types import DjangoDebugSQL
from .exception.types import DjangoDebugException
class DjangoDebug(ObjectType):
@ -8,3 +9,6 @@ class DjangoDebug(ObjectType):
description = "Debugging information for the current query."
sql = List(DjangoDebugSQL, description="Executed SQL queries for this API query.")
exceptions = List(
DjangoDebugException, description="Raise exceptions for this API query."
)

Loading…
Cancel
Save