Skip to content

Commit e76679f

Browse files
committed
MP-432 fix graphene logger
1 parent 23b90be commit e76679f

File tree

3 files changed

+64
-61
lines changed

3 files changed

+64
-61
lines changed

README.md

Lines changed: 46 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -21,29 +21,54 @@
2121
1. Add `GoogleFormatter` to your Django's `LOGGING` setting.
2222
Example:
2323
```python
24-
LOGGING = {
25-
"version": 1,
26-
"disable_existing_loggers": False,
27-
"formatters": {
28-
"json": {
29-
"()": "django_google_structured_logger.formatter.GoogleFormatter",
30-
},
31-
},
32-
"handlers": {
33-
"google-json-handler": {
34-
"class": "logging.StreamHandler",
35-
"formatter": "json",
36-
},
37-
},
38-
"root": {
39-
"handlers": ["google-json-handler"],
40-
"level": logging.INFO,
41-
}
42-
}
24+
LOGGING = {
25+
"version": 1,
26+
"disable_existing_loggers": False,
27+
"formatters": {
28+
"json": {
29+
"()": "django_google_structured_logger.formatter.GoogleFormatter",
30+
},
31+
},
32+
"handlers": {
33+
"console": {
34+
"level": "INFO",
35+
"class": "logging.StreamHandler",
36+
},
37+
"google-json-handler": {
38+
"class": "logging.StreamHandler",
39+
"formatter": "json",
40+
},
41+
},
42+
"root": {
43+
"handlers": [env.str("DJANGO_LOG_HANDLER", "google-json-handler")],
44+
"level": env.str("ROOT_LOG_LEVEL", "INFO"),
45+
},
46+
"loggers": {
47+
"()": {
48+
"handlers": [env.str("DJANGO_LOG_HANDLER", "google-json-handler")],
49+
"level": env.str("DJANGO_LOG_LEVEL", "INFO"),
50+
},
51+
"django": {
52+
"handlers": [env.str("DJANGO_LOG_HANDLER", "google-json-handler")],
53+
"level": env.str("DJANGO_LOG_LEVEL", "INFO"),
54+
"propagate": False,
55+
},
56+
"django.server": {
57+
"handlers": [env.str("DJANGO_LOG_HANDLER", "google-json-handler")],
58+
"level": env.str("DJANGO_SERVER_LEVEL", "ERROR"),
59+
"propagate": False,
60+
},
61+
"django.request": {
62+
"handlers": [env.str("DJANGO_LOG_HANDLER", "google-json-handler")],
63+
"level": env.str("DJANGO_REQUEST_LEVEL", "ERROR"),
64+
"propagate": False,
65+
},
66+
},
67+
}
4368
```
4469
2. Add `SetRequestToLoggerMiddleware` to your Django's `MIDDLEWARE` setting.
4570

46-
Example for Django middleware:
71+
Django middleware:
4772
```python
4873
MIDDLEWARE = [
4974
...
@@ -52,14 +77,12 @@
5277
"django_google_structured_logger.middlewares.LogRequestAndResponseMiddleware", # Log request and response.
5378
]
5479
```
55-
Example for GRAPHENE middleware:
80+
GRAPHENE middleware:
5681
```python
5782
GRAPHENE = {
5883
"MIDDLEWARE": [
5984
...
60-
# Ordering is important:
6185
"django_google_structured_logger.graphene_middlewares.GrapheneSetUserContextMiddleware", # Set user context to logger.
62-
"django_google_structured_logger.graphene_middlewares.GrapheneLogRequestAndResponseMiddleware", # Log request and response.
6386
]
6487
}
6588
```
Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
1-
import json
21
import logging
32
import uuid
43
from typing import Any
54

6-
from django.http import HttpResponse
7-
85
from . import settings
9-
from .middlewares import LogRequestAndResponseMiddleware
106
from .storages import RequestStorage, _current_request
117

128
logger = logging.getLogger(__name__)
@@ -31,26 +27,3 @@ def resolve(self, next, root, info, **args):
3127
@staticmethod
3228
def _get_user_attribute(user, attribute) -> Any:
3329
return getattr(user, attribute, None)
34-
35-
36-
class GrapheneLogRequestAndResponseMiddleware(LogRequestAndResponseMiddleware):
37-
def resolve(self, next, root, info, **args):
38-
if not settings.LOG_MIDDLEWARE_ENABLED:
39-
return next(root, info, **args)
40-
41-
# Graphene middleware doesn't give access to the raw request/response
42-
# Instead, the `info` argument provides a `context` attribute which usually contains the request
43-
request = info.context
44-
45-
self.process_request(request)
46-
47-
# Since there's no direct access to the response,
48-
# we can't process the response in the same way.
49-
# But we can capture the result of the GraphQL execution.
50-
result = next(root, info, **args)
51-
# Here, `result` is the data returned from your GraphQL resolvers.
52-
# We're wrapping it in a Django HttpResponse to use the existing process_response function.
53-
fake_response = HttpResponse(content=json.dumps(result))
54-
self.process_response(request, fake_response)
55-
56-
return result

django_google_structured_logger/middlewares.py

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ class LogRequestAndResponseMiddleware:
4141

4242
def __init__(self, get_response):
4343
self.get_response = get_response
44-
self.request_body = None
4544
self.log_excluded_headers_set = set(
4645
map(str.lower, settings.LOG_EXCLUDED_HEADERS)
4746
)
@@ -50,9 +49,8 @@ def __call__(self, request: HttpRequest) -> HttpResponse:
5049
if not settings.LOG_MIDDLEWARE_ENABLED:
5150
return self.get_response(request)
5251

53-
self.request_body = getattr(request, "body", None)
54-
response = self.get_response(request)
5552
self.process_request(request)
53+
response = self.get_response(request)
5654
self.process_response(request, response)
5755
return response
5856

@@ -70,15 +68,17 @@ def process_request(self, request):
7068
try:
7169
path = self._empty_value_none(getattr(request, "path", None))
7270
method = self._empty_value_none(getattr(request, "method", None))
71+
content_type = self._empty_value_none(
72+
getattr(request, "content_type", None)
73+
)
74+
request_body = self._empty_value_none(getattr(request, "body", None))
7375
request_data = {
7476
"request": {
75-
"body": self._get_request_body(request),
77+
"body": self._get_request_body(content_type, request_body),
7678
"query_params": self._empty_value_none(
7779
getattr(request, "GET", None)
7880
),
79-
"content_type": self._empty_value_none(
80-
getattr(request, "content_type", None)
81-
),
81+
"content_type": content_type,
8282
"method": method,
8383
"path": path,
8484
"headers": self._empty_value_none(
@@ -108,6 +108,14 @@ def process_response(self, request, response):
108108

109109
try:
110110
response_data = self._abridge(getattr(response, "data", None))
111+
if response_data is None:
112+
response_content = self._abridge(getattr(response, "content", None))
113+
content_type = self._empty_value_none(
114+
getattr(request, "content_type", None)
115+
)
116+
response_data = (
117+
self._get_request_body(content_type, response_content),
118+
)
111119
response_status_code = getattr(response, "status_code", 0)
112120
response_headers = self._exclude_keys(getattr(response, "headers", None))
113121

@@ -255,15 +263,14 @@ def _exclude_keys(self, obj: Optional[Dict]) -> Optional[Dict]:
255263
if k.lower() not in self.log_excluded_headers_set
256264
}
257265

258-
def _get_request_body(self, request) -> Union[str, Dict, None]:
266+
def _get_request_body(self, content_type, request_body) -> Union[str, Dict, None]:
259267
"""
260268
Extract request body and mask sensitive data.
261269
262270
Example:
263271
Input: Django request object with JSON body {"key": "value" }
264272
Output: {"key": "value"}
265273
"""
266-
content_type = getattr(request, "content_type", None)
267274

268275
def decode_and_abridge(body_bytes):
269276
body_str = body_bytes.decode("UTF-8") if body_bytes else None
@@ -276,9 +283,9 @@ def decode_and_abridge(body_bytes):
276283
if content_type == "multipart/form-data":
277284
return "The image was uploaded to the server"
278285
elif content_type == "application/json":
279-
return self._mask_sensitive_data(decode_and_abridge(self.request_body))
286+
return self._mask_sensitive_data(decode_and_abridge(request_body))
280287
elif content_type == "text/plain":
281-
return self._mask_sensitive_data(self._abridge(self.request_body))
288+
return self._mask_sensitive_data(self._abridge(request_body))
282289
else:
283290
return self._mask_sensitive_data(content_type)
284291

0 commit comments

Comments
 (0)