Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix transaction tracing when running Django under ASGI #1953

Open
jsma opened this issue Jan 16, 2024 · 4 comments
Open

Fix transaction tracing when running Django under ASGI #1953

jsma opened this issue Jan 16, 2024 · 4 comments
Labels
agent-python community Issues opened by the community triage Issues awaiting triage

Comments

@jsma
Copy link
Contributor

jsma commented Jan 16, 2024

(Moved from original discussion)

When running Django under ASGI, the middleware that Elastic APM injects is not tracking standard Django request transactions.

Here's my asgi.py:

django_asgi_app = get_asgi_application()

application = ProtocolTypeRouter(
    {
        "http": django_asgi_app,
    }
)

My settings.ELASTIC_APM:

{'SERVICE_NAME': 'myapp',
 'SERVER_URL': 'http://fleet-server:8200',
 'SECRET_TOKEN': '*************',
 'ENVIRONMENT': 'development',
 'DEBUG': True}

I've browsed several pages and waited for Fleet to flush but all I see are Celery tasks:

Screenshot 2024-01-02 at 3 52 41 PM

Nothing is logged as a request transaction type by the standard Django middleware when running under Daphne.

If I switch the project back to standard WSGI/Django runserver, it logs request transactions:

image
@github-actions github-actions bot added agent-python community Issues opened by the community triage Issues awaiting triage labels Jan 16, 2024
@MWedl
Copy link

MWedl commented Mar 11, 2024

We encountered this bug after updating to Django 5.0. We also run django in ASGI mode. In Django 4.x, transaction tracing worked fine, even in ASGI mode.

@benclark158
Copy link

I have encountered this as well in Django 5.0. When I call the following within my view class

import elasticapm
elasticapm.label(obj_id='id')

I get this message: Ignored labels obj_id. No transaction currently active. But when I downgrade to django 4.2.13 it works as expected

@xrmx
Copy link
Member

xrmx commented May 10, 2024

Created a new django 5.0.6 project.

Added in settings.py:

ELASTIC_APM = {
    "SERVICE_NAME": "djangoasgi",
    "SERVER_URL": "",
    "API_KEY": "",
    "DEBUG": True,
}

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'elasticapm.contrib.django',
]

Added in views:

from django.http import HttpResponse
import elasticapm


async def hello(request):
    elasticapm.set_transaction_name("djangoasgi.hello")
    elasticapm.label(is_asgi=True)
    return HttpResponse("Hello")

In urls.py:

from django.contrib import admin
from django.urls import path
from . import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('hello', views.hello),
]

in asgi.py:

import os

from django.core.asgi import get_asgi_application

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'djangoasgi.settings')

from elasticapm.contrib.asgi import ASGITracingMiddleware

application = ASGITracingMiddleware(get_asgi_application())

run it via gunicorn djangoasgi.asgi:application -w 1 -k uvicorn.workers.UvicornWorker

And got

Schermata del 2024-05-10 11-24-51

@benclark158
Copy link

Thank you @xrmx doing that and extending your code fixed the issues I was having. I adapted your solution to use a custom middleware as I had a lot of URLs to monitor.

I have found that the config option TRANSACTION_IGNORE_URLS didn't work with your solution though. I did some customisation to get it working with the middleware and ASGI middleware:

middleware.py

import elasticapm
from elasticapm.contrib.django.middleware import TracingMiddleware

class CustomTracingMiddleware(TracingMiddleware):

    def process_request(self, request: HttpRequest):
        
        if elasticapm.get_transaction_id():
            transaction_name: str = "{} {}".format(request.method.upper(), request.path.lower()) #type: ignore
            elasticapm.set_transaction_name(transaction_name) #type: ignore

        return None

asgi.py

from elasticapm.contrib.asgi import ASGITracingMiddleware

class CustomASGITracingMiddleware(ASGITracingMiddleware):
    def get_url(self, scope, host: str | None = None) -> Tuple[str, dict[str, str]]:
        _, url_dict = super().get_url(scope, host)
        path = scope.get("root_path", "") + scope.get("path", "")
        return path, url_dict

application = CustomASGITracingMiddleware(get_asgi_application())

settings.py

MIDDLEWARE = [
    'ehs_api.middleware.CustomTracingMiddleware',
    # add others here
]

ELASTIC_APM = {
    'SERVICE_NAME': 'djangoapm',
    'SERVER_URL': '',
    'DEBUG': True, #allows it to run in test mode
    'TRANSACTION_IGNORE_URLS': [
        '/__debug__/*',
    ],
    'DJANGO_TRANSACTION_NAME_FROM_ROUTE': True,
    'DJANGO_AUTOINSERT_MIDDLEWARE': False
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
agent-python community Issues opened by the community triage Issues awaiting triage
Projects
None yet
Development

No branches or pull requests

4 participants