This commit is contained in:
Yukio Mizuta 2025-03-13 22:32:56 +09:00 committed by GitHub
commit 4e53ab008a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 164 additions and 0 deletions

View File

@ -91,3 +91,21 @@ decorator:
# ...
path("graphql", csrf_exempt(GraphQLView.as_view(graphiql=True))),
]
Logging Errors
--------------
By default, when there is a GraphQL error because of client inputs , Django logs a very simple message
with 4xx HTTP status code. If you would like to see more details, you can enable
``ClientErrorLogMiddleware`` as follows:
.. code:: python
# settings.py
MIDDLEWARE = [
"graphene_django.middlewares.ClientErrorLogMiddleware",
# ...
]
This middleware works when your endpoint is ``/graphql``.

View File

@ -0,0 +1,39 @@
import json
import logging
from django.utils.log import log_response
logger = logging.getLogger("django.graphene")
class ClientErrorLogMiddleware:
"""
Logs graphql requests 4xx errors. (Except 401, 403)
"""
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
try:
if (
400 <= response.status_code < 500
and response.status_code not in (401, 403)
and "graphql" in request.path.lower()
):
response_json = json.loads(response.content)
if "errors" in response_json:
log_response(
message=(
f"Graphql Error: {response_json['errors']}\n"
f"The Query is: {json.loads(request.body)}"
),
response=response,
)
except Exception:
logger.error(f"Error logging graphql error.", exc_info=True)
return response

View File

@ -0,0 +1,107 @@
import json
import logging
import graphene
import mock
from django.http.response import HttpResponse
from django.test import RequestFactory
from graphene.test import Client
from .models import Reporter
from .. import DjangoObjectType
from ..middlewares import ClientErrorLogMiddleware
class ReporterType(DjangoObjectType):
class Meta:
model = Reporter
fields = "__all__"
class Query(graphene.ObjectType):
reporter = graphene.Field(ReporterType)
def resolve_reporter(self, info, **args):
return Reporter.objects.first()
def test_should_log_error(caplog):
Reporter.objects.create(last_name="ABA")
invalid_query = """
query ReporterQuery {
reporter {
invalidAttrName
}
}
"""
schema = graphene.Schema(query=Query)
client = Client(schema)
response = client.execute(invalid_query)
factory = RequestFactory()
request = factory.post(
"/graphql", data=json.dumps(invalid_query), content_type="application/json"
)
http_res = HttpResponse(json.dumps(response).encode(), status=400)
get_response = mock.MagicMock()
get_response.return_value = http_res
middleware = ClientErrorLogMiddleware(get_response)
middleware(request)
assert len(caplog.records) == 1
assert caplog.records[0] != "WARNING"
assert str(response["errors"]) in caplog.text
assert invalid_query in caplog.text
def test_should_not_log_success(caplog):
Reporter.objects.create(last_name="ABA")
valid_query = """
query ReporterQuery {
reporter {
lastName
}
}
"""
schema = graphene.Schema(query=Query)
client = Client(schema)
response = client.execute(valid_query)
factory = RequestFactory()
request = factory.post(
"/graphql", data=json.dumps(valid_query), content_type="application/json"
)
http_res = HttpResponse(json.dumps(response).encode(), status=200)
get_response = mock.MagicMock()
get_response.return_value = http_res
middleware = ClientErrorLogMiddleware(get_response)
middleware(request)
assert len(caplog.records) == 0
def test_should_not_log_non_graphql_error(caplog):
factory = RequestFactory()
request = factory.post(
"/users", data=json.dumps({"name": "Mario"}), content_type="application/json"
)
http_res = HttpResponse(
json.dumps({"errors": ["Got to be Luigi"]}).encode(), status=400
)
get_response = mock.MagicMock()
get_response.return_value = http_res
middleware = ClientErrorLogMiddleware(get_response)
middleware(request)
assert len(caplog.records) == 0