Django integration doesn't work for async projects #796
Open
Description
I don't have time to make a proper PR soonish but here's a version of the middleware which works for both sync and async contexts:
from asgiref.sync import iscoroutinefunction
from django.conf import settings
from datetime import datetime
import time
from django.conf import settings
from readme_metrics.Metrics import Metrics
from readme_metrics.ResponseInfoWrapper import ResponseInfoWrapper
from readme_metrics import MetricsApiConfig
from django.utils.decorators import sync_and_async_middleware
@sync_and_async_middleware
def metrics_middleware(get_response):
# One-time configuration and initialization goes here.
get_response = get_response
config = settings.README_METRICS_CONFIG
assert isinstance(config, MetricsApiConfig)
metrics_core = Metrics(config)
def preamble(request):
try:
request.rm_start_dt = datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ")
request.rm_start_ts = int(time.time() * 1000)
if request.headers.get("Content-Length") or request.body:
request.rm_content_length = request.headers["Content-Length"] or "0"
request.rm_body = request.body or ""
except Exception as e:
# Errors in the Metrics SDK should never cause the application to
# throw an error. Log it but don't re-raise.
config.LOGGER.exception(e)
def handle_response(request, response):
try:
try:
body = response.content.decode("utf-8")
except UnicodeDecodeError:
body = "[NOT VALID UTF-8]"
response_info = ResponseInfoWrapper(
response.headers,
response.status_code,
content_type=None,
content_length=None,
body=body,
)
metrics_core.process(request, response_info)
except Exception as e:
# Errors in the Metrics SDK should never cause the application to
# throw an error. Log it but don't re-raise.
config.LOGGER.exception(e)
if iscoroutinefunction(get_response):
async def middleware(request):
print("async metrics middleware")
preamble(request)
response = await get_response(request)
handle_response(request, response)
return response
else:
def middleware(request):
print("sync metrics middleware")
preamble(request)
response = get_response(request)
handle_response(request, response)
return response
return middleware
Relevant documentation: https://docs.djangoproject.com/en/4.2/topics/http/middleware/#async-middleware
In addition, readme_metrics.Metrics.Metrics
tries to access request.environ
which is WSGI specific. For my use case, I just replaced it with a patched version which uses request.META
instead. For your package I imagine you'll want to handle both WSGI and ASGI gracefully.