mirror of
				https://github.com/graphql-python/graphene-django.git
				synced 2025-10-31 16:07:36 +03:00 
			
		
		
		
	Add ClientErrorLogMiddleware for detailed logging
This commit is contained in:
		
							parent
							
								
									c3404a9793
								
							
						
					
					
						commit
						54bb9371af
					
				|  | @ -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``. | ||||
|  |  | |||
							
								
								
									
										39
									
								
								graphene_django/middlewares.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								graphene_django/middlewares.py
									
									
									
									
									
										Normal 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 | ||||
							
								
								
									
										107
									
								
								graphene_django/tests/test_middlewares.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								graphene_django/tests/test_middlewares.py
									
									
									
									
									
										Normal 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 | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user