Repository: DataDog/dd-trace-py

Issue:
New Django integration: opt-out of middleware instrumentation
I'm currently trying the new Django integration introduced in #1161 while upgrading from 0.31.0 to 0.35.0 and have some feedback.

The new middleware instrumentation seems like a good idea, but in our case, it's causing our flame graph to become bloated with a lot of traces. We have ~30 middleware installed and they easily look like this, making them hard to navigate:

<img width="362" alt="Screenshot 2020-04-08 at 17 52 20" src="https://user-images.githubusercontent.com/861044/78811433-c771e280-79c1-11ea-880f-21151ab562a1.png">

(This example is for a trace coming from our front-end app making several API calls to our Django BE)

I appreciate that it might help folks in other situations or maybe when we need to debug an issue with middleware, but I was wondering if there was a setting to disable this? And if not, could we add one?

I think we would be ok for us to either have all middleware grouped under a single layer or included in the Django layer. There might be other options I haven't though of too, I'm open to suggestions.

### Which version of dd-trace-py are you using?

ddtrace 0.35.0

### Which version of the libraries are you using?

Django 2.2.11

### How can we reproduce your problem?

Have an app with lots of middlewares, check traces.



Discussion:

Resolving patch:
diff --git a/ddtrace/contrib/django/__init__.py b/ddtrace/contrib/django/__init__.py
--- a/ddtrace/contrib/django/__init__.py
+++ b/ddtrace/contrib/django/__init__.py
@@ -69,6 +69,14 @@
 
    Default: ``''``
 
+.. py:data:: ddtrace.config.django['instrument_middleware']
+
+   Whether or not to instrument middleware.
+
+   Can also be enabled with the ``DD_DJANGO_INSTRUMENT_MIDDLEWARE`` environment variable.
+
+   Default: ``True``
+
 .. py:data:: ddtrace.config.django['instrument_databases']
 
    Whether or not to instrument databases.
diff --git a/ddtrace/contrib/django/patch.py b/ddtrace/contrib/django/patch.py
--- a/ddtrace/contrib/django/patch.py
+++ b/ddtrace/contrib/django/patch.py
@@ -18,7 +18,7 @@
 from ddtrace.ext import http, sql as sqlx, SpanTypes
 from ddtrace.internal.logger import get_logger
 from ddtrace.propagation.http import HTTPPropagator
-from ddtrace.utils.formats import get_env
+from ddtrace.utils.formats import asbool, get_env
 from ddtrace.utils.wrappers import unwrap, iswrapped
 
 from .compat import get_resolver, user_is_authenticated
@@ -35,6 +35,7 @@
         cache_service_name=get_env("django", "cache_service_name") or "django",
         database_service_name_prefix=get_env("django", "database_service_name_prefix", default=""),
         distributed_tracing_enabled=True,
+        instrument_middleware=asbool(get_env("django", "instrument_middleware", default=True)),
         instrument_databases=True,
         instrument_caches=True,
         analytics_enabled=None,  # None allows the value to be overridden by the global config
@@ -515,7 +516,10 @@ def _patch(django):
     # DEV: this check will be replaced with import hooks in the future
     if "django.core.handlers.base" not in sys.modules:
         import django.core.handlers.base
-    wrap(django, "core.handlers.base.BaseHandler.load_middleware", traced_load_middleware(django))
+
+    if config.django.instrument_middleware:
+        wrap(django, "core.handlers.base.BaseHandler.load_middleware", traced_load_middleware(django))
+
     wrap(django, "core.handlers.base.BaseHandler.get_response", traced_get_response(django))
 
     # DEV: this check will be replaced with import hooks in the future


Test patch: