Skip to content

Django integration doesn't work for async projects #796

Open
@reuben

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.

Metadata

Assignees

No one assigned

    Labels

    pythonIssues related to our Python SDK

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions