We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Add support for Django 3.2
Django 3.2 is slated for a release in April. Running the test suite, a few problems came up:

- [ ] App label needs to be a valid Python identifier, ours is not (renaming it from `elasticapm.contrib.django` to `elasticapm` should suffice)

Several test failures:

- [ ] `test_broken_500_handler_with_middleware`
- [ ] `test_404_middleware`
- [ ] `test_response_error_id_middleware`
- [ ] `test_django_logging_request_kwarg`
- [ ] `test_django_logging_middleware`
- [ ] `test_capture_body_config_is_dynamic_for_transactions`
- [ ] `test_capture_headers_config_is_dynamic_for_transactions`
- [ ] `test_capture_headers`
- [ ] `test_transaction_name_from_route`

Most of these look similar in nature, I suspect an issue with middlewares. Nothing jumps out in the [release notes](https://docs.djangoproject.com/en/3.2/releases/3.2/), though.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `elasticapm/contrib/django/apps.py`
Content:
```
1 #  BSD 3-Clause License
2 #
3 #  Copyright (c) 2019, Elasticsearch BV
4 #  All rights reserved.
5 #
6 #  Redistribution and use in source and binary forms, with or without
7 #  modification, are permitted provided that the following conditions are met:
8 #
9 #  * Redistributions of source code must retain the above copyright notice, this
10 #    list of conditions and the following disclaimer.
11 #
12 #  * Redistributions in binary form must reproduce the above copyright notice,
13 #    this list of conditions and the following disclaimer in the documentation
14 #    and/or other materials provided with the distribution.
15 #
16 #  * Neither the name of the copyright holder nor the names of its
17 #    contributors may be used to endorse or promote products derived from
18 #    this software without specific prior written permission.
19 #
20 #  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 #  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 #  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 #  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 #  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 #  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 #  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 #  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 #  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 #  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 
31 from functools import partial
32 
33 from django.apps import AppConfig
34 from django.conf import settings as django_settings
35 
36 from elasticapm.conf import constants
37 from elasticapm.contrib.django.client import get_client
38 from elasticapm.utils.disttracing import TraceParent
39 from elasticapm.utils.logging import get_logger
40 from elasticapm.utils.wsgi import get_current_url
41 
42 logger = get_logger("elasticapm.traces")
43 
44 ERROR_DISPATCH_UID = "elasticapm-exceptions"
45 REQUEST_START_DISPATCH_UID = "elasticapm-request-start"
46 REQUEST_FINISH_DISPATCH_UID = "elasticapm-request-stop"
47 
48 MIDDLEWARE_NAME = "elasticapm.contrib.django.middleware.TracingMiddleware"
49 
50 TRACEPARENT_HEADER_NAME_WSGI = "HTTP_" + constants.TRACEPARENT_HEADER_NAME.upper().replace("-", "_")
51 TRACEPARENT_LEGACY_HEADER_NAME_WSGI = "HTTP_" + constants.TRACEPARENT_LEGACY_HEADER_NAME.upper().replace("-", "_")
52 TRACESTATE_HEADER_NAME_WSGI = "HTTP_" + constants.TRACESTATE_HEADER_NAME.upper().replace("-", "_")
53 
54 
55 class ElasticAPMConfig(AppConfig):
56     name = "elasticapm.contrib.django"
57     label = "elasticapm.contrib.django"
58     verbose_name = "ElasticAPM"
59 
60     def __init__(self, *args, **kwargs):
61         super(ElasticAPMConfig, self).__init__(*args, **kwargs)
62         self.client = None
63 
64     def ready(self):
65         self.client = get_client()
66         if self.client.config.autoinsert_django_middleware:
67             self.insert_middleware(django_settings)
68         register_handlers(self.client)
69         if self.client.config.instrument and self.client.config.enabled:
70             instrument(self.client)
71         else:
72             self.client.logger.debug("Skipping instrumentation. INSTRUMENT is set to False.")
73 
74     @staticmethod
75     def insert_middleware(settings):
76         if hasattr(settings, "MIDDLEWARE"):
77             middleware_list = settings.MIDDLEWARE
78             middleware_attr = "MIDDLEWARE"
79         elif hasattr(settings, "MIDDLEWARE_CLASSES"):  # can be removed when we drop support for Django 1.x
80             middleware_list = settings.MIDDLEWARE_CLASSES
81             middleware_attr = "MIDDLEWARE_CLASSES"
82         else:
83             logger.debug("Could not find middleware setting, not autoinserting tracing middleware")
84             return
85         is_tuple = isinstance(middleware_list, tuple)
86         if is_tuple:
87             middleware_list = list(middleware_list)
88         elif not isinstance(middleware_list, list):
89             logger.debug("%s setting is not of type list or tuple, not autoinserting tracing middleware")
90             return
91         if middleware_list is not None and MIDDLEWARE_NAME not in middleware_list:
92             logger.debug("Inserting tracing middleware into settings.%s", middleware_attr)
93             middleware_list.insert(0, MIDDLEWARE_NAME)
94         if is_tuple:
95             middleware_list = tuple(middleware_list)
96         if middleware_list:
97             setattr(settings, middleware_attr, middleware_list)
98 
99 
100 def register_handlers(client):
101     from django.core.signals import got_request_exception, request_finished, request_started
102 
103     from elasticapm.contrib.django.handlers import exception_handler
104 
105     # Connect to Django's internal signal handlers
106     got_request_exception.disconnect(dispatch_uid=ERROR_DISPATCH_UID)
107     got_request_exception.connect(partial(exception_handler, client), dispatch_uid=ERROR_DISPATCH_UID, weak=False)
108 
109     request_started.disconnect(dispatch_uid=REQUEST_START_DISPATCH_UID)
110     request_started.connect(
111         partial(_request_started_handler, client), dispatch_uid=REQUEST_START_DISPATCH_UID, weak=False
112     )
113 
114     request_finished.disconnect(dispatch_uid=REQUEST_FINISH_DISPATCH_UID)
115     request_finished.connect(
116         lambda sender, **kwargs: client.end_transaction() if _should_start_transaction(client) else None,
117         dispatch_uid=REQUEST_FINISH_DISPATCH_UID,
118         weak=False,
119     )
120 
121     # If we can import celery, register ourselves as exception handler
122     try:
123         import celery  # noqa F401
124 
125         from elasticapm.contrib.celery import register_exception_tracking
126 
127         try:
128             register_exception_tracking(client)
129         except Exception as e:
130             client.logger.exception("Failed installing django-celery hook: %s" % e)
131     except ImportError:
132         client.logger.debug("Not instrumenting Celery, couldn't import")
133 
134 
135 def _request_started_handler(client, sender, *args, **kwargs):
136     if not _should_start_transaction(client):
137         return
138     # try to find trace id
139     trace_parent = None
140     if "environ" in kwargs:
141         url = get_current_url(kwargs["environ"], strip_querystring=True, path_only=True)
142         if client.should_ignore_url(url):
143             logger.debug("Ignoring request due to %s matching transaction_ignore_urls")
144             return
145         trace_parent = TraceParent.from_headers(
146             kwargs["environ"],
147             TRACEPARENT_HEADER_NAME_WSGI,
148             TRACEPARENT_LEGACY_HEADER_NAME_WSGI,
149             TRACESTATE_HEADER_NAME_WSGI,
150         )
151     elif "scope" in kwargs:
152         scope = kwargs["scope"]
153         fake_environ = {"SCRIPT_NAME": scope.get("root_path", ""), "PATH_INFO": scope["path"], "QUERY_STRING": ""}
154         url = get_current_url(fake_environ, strip_querystring=True, path_only=True)
155         if client.should_ignore_url(url):
156             logger.debug("Ignoring request due to %s matching transaction_ignore_urls")
157             return
158         if "headers" in scope:
159             trace_parent = TraceParent.from_headers(scope["headers"])
160     client.begin_transaction("request", trace_parent=trace_parent)
161 
162 
163 def instrument(client):
164     """
165     Auto-instruments code to get nice spans
166     """
167     from elasticapm.instrumentation.control import instrument
168 
169     instrument()
170     try:
171         import celery  # noqa F401
172 
173         from elasticapm.contrib.celery import register_instrumentation
174 
175         register_instrumentation(client)
176     except ImportError:
177         client.logger.debug("Not instrumenting Celery, couldn't import")
178 
179 
180 def _should_start_transaction(client):
181     middleware_attr = "MIDDLEWARE" if getattr(django_settings, "MIDDLEWARE", None) is not None else "MIDDLEWARE_CLASSES"
182     middleware = getattr(django_settings, middleware_attr)
183     return (
184         (not django_settings.DEBUG or client.config.debug)
185         and middleware
186         and "elasticapm.contrib.django.middleware.TracingMiddleware" in middleware
187     )
188
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/elasticapm/contrib/django/apps.py b/elasticapm/contrib/django/apps.py
--- a/elasticapm/contrib/django/apps.py
+++ b/elasticapm/contrib/django/apps.py
@@ -54,7 +54,7 @@
 
 class ElasticAPMConfig(AppConfig):
     name = "elasticapm.contrib.django"
-    label = "elasticapm.contrib.django"
+    label = "elasticapm"
     verbose_name = "ElasticAPM"
 
     def __init__(self, *args, **kwargs):

{"golden_diff": "diff --git a/elasticapm/contrib/django/apps.py b/elasticapm/contrib/django/apps.py\n--- a/elasticapm/contrib/django/apps.py\n+++ b/elasticapm/contrib/django/apps.py\n@@ -54,7 +54,7 @@\n \n class ElasticAPMConfig(AppConfig):\n     name = \"elasticapm.contrib.django\"\n-    label = \"elasticapm.contrib.django\"\n+    label = \"elasticapm\"\n     verbose_name = \"ElasticAPM\"\n \n     def __init__(self, *args, **kwargs):\n", "issue": "Add support for Django 3.2\nDjango 3.2 is slated for a release in April. Running the test suite, a few problems came up:\r\n\r\n- [ ] App label needs to be a valid Python identifier, ours is not (renaming it from `elasticapm.contrib.django` to `elasticapm` should suffice)\r\n\r\nSeveral test failures:\r\n\r\n- [ ] `test_broken_500_handler_with_middleware`\r\n- [ ] `test_404_middleware`\r\n- [ ] `test_response_error_id_middleware`\r\n- [ ] `test_django_logging_request_kwarg`\r\n- [ ] `test_django_logging_middleware`\r\n- [ ] `test_capture_body_config_is_dynamic_for_transactions`\r\n- [ ] `test_capture_headers_config_is_dynamic_for_transactions`\r\n- [ ] `test_capture_headers`\r\n- [ ] `test_transaction_name_from_route`\r\n\r\nMost of these look similar in nature, I suspect an issue with middlewares. Nothing jumps out in the [release notes](https://docs.djangoproject.com/en/3.2/releases/3.2/), though.\r\n\n", "before_files": [{"content": "#  BSD 3-Clause License\n#\n#  Copyright (c) 2019, Elasticsearch BV\n#  All rights reserved.\n#\n#  Redistribution and use in source and binary forms, with or without\n#  modification, are permitted provided that the following conditions are met:\n#\n#  * Redistributions of source code must retain the above copyright notice, this\n#    list of conditions and the following disclaimer.\n#\n#  * Redistributions in binary form must reproduce the above copyright notice,\n#    this list of conditions and the following disclaimer in the documentation\n#    and/or other materials provided with the distribution.\n#\n#  * Neither the name of the copyright holder nor the names of its\n#    contributors may be used to endorse or promote products derived from\n#    this software without specific prior written permission.\n#\n#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n#  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n#  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n#  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n#  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n#  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n#  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n#  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\nfrom functools import partial\n\nfrom django.apps import AppConfig\nfrom django.conf import settings as django_settings\n\nfrom elasticapm.conf import constants\nfrom elasticapm.contrib.django.client import get_client\nfrom elasticapm.utils.disttracing import TraceParent\nfrom elasticapm.utils.logging import get_logger\nfrom elasticapm.utils.wsgi import get_current_url\n\nlogger = get_logger(\"elasticapm.traces\")\n\nERROR_DISPATCH_UID = \"elasticapm-exceptions\"\nREQUEST_START_DISPATCH_UID = \"elasticapm-request-start\"\nREQUEST_FINISH_DISPATCH_UID = \"elasticapm-request-stop\"\n\nMIDDLEWARE_NAME = \"elasticapm.contrib.django.middleware.TracingMiddleware\"\n\nTRACEPARENT_HEADER_NAME_WSGI = \"HTTP_\" + constants.TRACEPARENT_HEADER_NAME.upper().replace(\"-\", \"_\")\nTRACEPARENT_LEGACY_HEADER_NAME_WSGI = \"HTTP_\" + constants.TRACEPARENT_LEGACY_HEADER_NAME.upper().replace(\"-\", \"_\")\nTRACESTATE_HEADER_NAME_WSGI = \"HTTP_\" + constants.TRACESTATE_HEADER_NAME.upper().replace(\"-\", \"_\")\n\n\nclass ElasticAPMConfig(AppConfig):\n    name = \"elasticapm.contrib.django\"\n    label = \"elasticapm.contrib.django\"\n    verbose_name = \"ElasticAPM\"\n\n    def __init__(self, *args, **kwargs):\n        super(ElasticAPMConfig, self).__init__(*args, **kwargs)\n        self.client = None\n\n    def ready(self):\n        self.client = get_client()\n        if self.client.config.autoinsert_django_middleware:\n            self.insert_middleware(django_settings)\n        register_handlers(self.client)\n        if self.client.config.instrument and self.client.config.enabled:\n            instrument(self.client)\n        else:\n            self.client.logger.debug(\"Skipping instrumentation. INSTRUMENT is set to False.\")\n\n    @staticmethod\n    def insert_middleware(settings):\n        if hasattr(settings, \"MIDDLEWARE\"):\n            middleware_list = settings.MIDDLEWARE\n            middleware_attr = \"MIDDLEWARE\"\n        elif hasattr(settings, \"MIDDLEWARE_CLASSES\"):  # can be removed when we drop support for Django 1.x\n            middleware_list = settings.MIDDLEWARE_CLASSES\n            middleware_attr = \"MIDDLEWARE_CLASSES\"\n        else:\n            logger.debug(\"Could not find middleware setting, not autoinserting tracing middleware\")\n            return\n        is_tuple = isinstance(middleware_list, tuple)\n        if is_tuple:\n            middleware_list = list(middleware_list)\n        elif not isinstance(middleware_list, list):\n            logger.debug(\"%s setting is not of type list or tuple, not autoinserting tracing middleware\")\n            return\n        if middleware_list is not None and MIDDLEWARE_NAME not in middleware_list:\n            logger.debug(\"Inserting tracing middleware into settings.%s\", middleware_attr)\n            middleware_list.insert(0, MIDDLEWARE_NAME)\n        if is_tuple:\n            middleware_list = tuple(middleware_list)\n        if middleware_list:\n            setattr(settings, middleware_attr, middleware_list)\n\n\ndef register_handlers(client):\n    from django.core.signals import got_request_exception, request_finished, request_started\n\n    from elasticapm.contrib.django.handlers import exception_handler\n\n    # Connect to Django's internal signal handlers\n    got_request_exception.disconnect(dispatch_uid=ERROR_DISPATCH_UID)\n    got_request_exception.connect(partial(exception_handler, client), dispatch_uid=ERROR_DISPATCH_UID, weak=False)\n\n    request_started.disconnect(dispatch_uid=REQUEST_START_DISPATCH_UID)\n    request_started.connect(\n        partial(_request_started_handler, client), dispatch_uid=REQUEST_START_DISPATCH_UID, weak=False\n    )\n\n    request_finished.disconnect(dispatch_uid=REQUEST_FINISH_DISPATCH_UID)\n    request_finished.connect(\n        lambda sender, **kwargs: client.end_transaction() if _should_start_transaction(client) else None,\n        dispatch_uid=REQUEST_FINISH_DISPATCH_UID,\n        weak=False,\n    )\n\n    # If we can import celery, register ourselves as exception handler\n    try:\n        import celery  # noqa F401\n\n        from elasticapm.contrib.celery import register_exception_tracking\n\n        try:\n            register_exception_tracking(client)\n        except Exception as e:\n            client.logger.exception(\"Failed installing django-celery hook: %s\" % e)\n    except ImportError:\n        client.logger.debug(\"Not instrumenting Celery, couldn't import\")\n\n\ndef _request_started_handler(client, sender, *args, **kwargs):\n    if not _should_start_transaction(client):\n        return\n    # try to find trace id\n    trace_parent = None\n    if \"environ\" in kwargs:\n        url = get_current_url(kwargs[\"environ\"], strip_querystring=True, path_only=True)\n        if client.should_ignore_url(url):\n            logger.debug(\"Ignoring request due to %s matching transaction_ignore_urls\")\n            return\n        trace_parent = TraceParent.from_headers(\n            kwargs[\"environ\"],\n            TRACEPARENT_HEADER_NAME_WSGI,\n            TRACEPARENT_LEGACY_HEADER_NAME_WSGI,\n            TRACESTATE_HEADER_NAME_WSGI,\n        )\n    elif \"scope\" in kwargs:\n        scope = kwargs[\"scope\"]\n        fake_environ = {\"SCRIPT_NAME\": scope.get(\"root_path\", \"\"), \"PATH_INFO\": scope[\"path\"], \"QUERY_STRING\": \"\"}\n        url = get_current_url(fake_environ, strip_querystring=True, path_only=True)\n        if client.should_ignore_url(url):\n            logger.debug(\"Ignoring request due to %s matching transaction_ignore_urls\")\n            return\n        if \"headers\" in scope:\n            trace_parent = TraceParent.from_headers(scope[\"headers\"])\n    client.begin_transaction(\"request\", trace_parent=trace_parent)\n\n\ndef instrument(client):\n    \"\"\"\n    Auto-instruments code to get nice spans\n    \"\"\"\n    from elasticapm.instrumentation.control import instrument\n\n    instrument()\n    try:\n        import celery  # noqa F401\n\n        from elasticapm.contrib.celery import register_instrumentation\n\n        register_instrumentation(client)\n    except ImportError:\n        client.logger.debug(\"Not instrumenting Celery, couldn't import\")\n\n\ndef _should_start_transaction(client):\n    middleware_attr = \"MIDDLEWARE\" if getattr(django_settings, \"MIDDLEWARE\", None) is not None else \"MIDDLEWARE_CLASSES\"\n    middleware = getattr(django_settings, middleware_attr)\n    return (\n        (not django_settings.DEBUG or client.config.debug)\n        and middleware\n        and \"elasticapm.contrib.django.middleware.TracingMiddleware\" in middleware\n    )\n", "path": "elasticapm/contrib/django/apps.py"}], "after_files": [{"content": "#  BSD 3-Clause License\n#\n#  Copyright (c) 2019, Elasticsearch BV\n#  All rights reserved.\n#\n#  Redistribution and use in source and binary forms, with or without\n#  modification, are permitted provided that the following conditions are met:\n#\n#  * Redistributions of source code must retain the above copyright notice, this\n#    list of conditions and the following disclaimer.\n#\n#  * Redistributions in binary form must reproduce the above copyright notice,\n#    this list of conditions and the following disclaimer in the documentation\n#    and/or other materials provided with the distribution.\n#\n#  * Neither the name of the copyright holder nor the names of its\n#    contributors may be used to endorse or promote products derived from\n#    this software without specific prior written permission.\n#\n#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n#  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n#  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n#  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n#  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n#  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n#  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n#  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\nfrom functools import partial\n\nfrom django.apps import AppConfig\nfrom django.conf import settings as django_settings\n\nfrom elasticapm.conf import constants\nfrom elasticapm.contrib.django.client import get_client\nfrom elasticapm.utils.disttracing import TraceParent\nfrom elasticapm.utils.logging import get_logger\nfrom elasticapm.utils.wsgi import get_current_url\n\nlogger = get_logger(\"elasticapm.traces\")\n\nERROR_DISPATCH_UID = \"elasticapm-exceptions\"\nREQUEST_START_DISPATCH_UID = \"elasticapm-request-start\"\nREQUEST_FINISH_DISPATCH_UID = \"elasticapm-request-stop\"\n\nMIDDLEWARE_NAME = \"elasticapm.contrib.django.middleware.TracingMiddleware\"\n\nTRACEPARENT_HEADER_NAME_WSGI = \"HTTP_\" + constants.TRACEPARENT_HEADER_NAME.upper().replace(\"-\", \"_\")\nTRACEPARENT_LEGACY_HEADER_NAME_WSGI = \"HTTP_\" + constants.TRACEPARENT_LEGACY_HEADER_NAME.upper().replace(\"-\", \"_\")\nTRACESTATE_HEADER_NAME_WSGI = \"HTTP_\" + constants.TRACESTATE_HEADER_NAME.upper().replace(\"-\", \"_\")\n\n\nclass ElasticAPMConfig(AppConfig):\n    name = \"elasticapm.contrib.django\"\n    label = \"elasticapm\"\n    verbose_name = \"ElasticAPM\"\n\n    def __init__(self, *args, **kwargs):\n        super(ElasticAPMConfig, self).__init__(*args, **kwargs)\n        self.client = None\n\n    def ready(self):\n        self.client = get_client()\n        if self.client.config.autoinsert_django_middleware:\n            self.insert_middleware(django_settings)\n        register_handlers(self.client)\n        if self.client.config.instrument and self.client.config.enabled:\n            instrument(self.client)\n        else:\n            self.client.logger.debug(\"Skipping instrumentation. INSTRUMENT is set to False.\")\n\n    @staticmethod\n    def insert_middleware(settings):\n        if hasattr(settings, \"MIDDLEWARE\"):\n            middleware_list = settings.MIDDLEWARE\n            middleware_attr = \"MIDDLEWARE\"\n        elif hasattr(settings, \"MIDDLEWARE_CLASSES\"):  # can be removed when we drop support for Django 1.x\n            middleware_list = settings.MIDDLEWARE_CLASSES\n            middleware_attr = \"MIDDLEWARE_CLASSES\"\n        else:\n            logger.debug(\"Could not find middleware setting, not autoinserting tracing middleware\")\n            return\n        is_tuple = isinstance(middleware_list, tuple)\n        if is_tuple:\n            middleware_list = list(middleware_list)\n        elif not isinstance(middleware_list, list):\n            logger.debug(\"%s setting is not of type list or tuple, not autoinserting tracing middleware\")\n            return\n        if middleware_list is not None and MIDDLEWARE_NAME not in middleware_list:\n            logger.debug(\"Inserting tracing middleware into settings.%s\", middleware_attr)\n            middleware_list.insert(0, MIDDLEWARE_NAME)\n        if is_tuple:\n            middleware_list = tuple(middleware_list)\n        if middleware_list:\n            setattr(settings, middleware_attr, middleware_list)\n\n\ndef register_handlers(client):\n    from django.core.signals import got_request_exception, request_finished, request_started\n\n    from elasticapm.contrib.django.handlers import exception_handler\n\n    # Connect to Django's internal signal handlers\n    got_request_exception.disconnect(dispatch_uid=ERROR_DISPATCH_UID)\n    got_request_exception.connect(partial(exception_handler, client), dispatch_uid=ERROR_DISPATCH_UID, weak=False)\n\n    request_started.disconnect(dispatch_uid=REQUEST_START_DISPATCH_UID)\n    request_started.connect(\n        partial(_request_started_handler, client), dispatch_uid=REQUEST_START_DISPATCH_UID, weak=False\n    )\n\n    request_finished.disconnect(dispatch_uid=REQUEST_FINISH_DISPATCH_UID)\n    request_finished.connect(\n        lambda sender, **kwargs: client.end_transaction() if _should_start_transaction(client) else None,\n        dispatch_uid=REQUEST_FINISH_DISPATCH_UID,\n        weak=False,\n    )\n\n    # If we can import celery, register ourselves as exception handler\n    try:\n        import celery  # noqa F401\n\n        from elasticapm.contrib.celery import register_exception_tracking\n\n        try:\n            register_exception_tracking(client)\n        except Exception as e:\n            client.logger.exception(\"Failed installing django-celery hook: %s\" % e)\n    except ImportError:\n        client.logger.debug(\"Not instrumenting Celery, couldn't import\")\n\n\ndef _request_started_handler(client, sender, *args, **kwargs):\n    if not _should_start_transaction(client):\n        return\n    # try to find trace id\n    trace_parent = None\n    if \"environ\" in kwargs:\n        url = get_current_url(kwargs[\"environ\"], strip_querystring=True, path_only=True)\n        if client.should_ignore_url(url):\n            logger.debug(\"Ignoring request due to %s matching transaction_ignore_urls\")\n            return\n        trace_parent = TraceParent.from_headers(\n            kwargs[\"environ\"],\n            TRACEPARENT_HEADER_NAME_WSGI,\n            TRACEPARENT_LEGACY_HEADER_NAME_WSGI,\n            TRACESTATE_HEADER_NAME_WSGI,\n        )\n    elif \"scope\" in kwargs:\n        scope = kwargs[\"scope\"]\n        fake_environ = {\"SCRIPT_NAME\": scope.get(\"root_path\", \"\"), \"PATH_INFO\": scope[\"path\"], \"QUERY_STRING\": \"\"}\n        url = get_current_url(fake_environ, strip_querystring=True, path_only=True)\n        if client.should_ignore_url(url):\n            logger.debug(\"Ignoring request due to %s matching transaction_ignore_urls\")\n            return\n        if \"headers\" in scope:\n            trace_parent = TraceParent.from_headers(scope[\"headers\"])\n    client.begin_transaction(\"request\", trace_parent=trace_parent)\n\n\ndef instrument(client):\n    \"\"\"\n    Auto-instruments code to get nice spans\n    \"\"\"\n    from elasticapm.instrumentation.control import instrument\n\n    instrument()\n    try:\n        import celery  # noqa F401\n\n        from elasticapm.contrib.celery import register_instrumentation\n\n        register_instrumentation(client)\n    except ImportError:\n        client.logger.debug(\"Not instrumenting Celery, couldn't import\")\n\n\ndef _should_start_transaction(client):\n    middleware_attr = \"MIDDLEWARE\" if getattr(django_settings, \"MIDDLEWARE\", None) is not None else \"MIDDLEWARE_CLASSES\"\n    middleware = getattr(django_settings, middleware_attr)\n    return (\n        (not django_settings.DEBUG or client.config.debug)\n        and middleware\n        and \"elasticapm.contrib.django.middleware.TracingMiddleware\" in middleware\n    )\n", "path": "elasticapm/contrib/django/apps.py"}]}