Repository: DataDog/dd-trace-py

Issue:
django.cache not categorized as app_type 'cache'?
We know it is getting reported just fine:

![](https://cdn.zapier.com/storage/photos/b1ae081f3ca372c8d8fe5cca6d95ec1a.png)

But if you look at the type tab for cache:

![](https://cdn.zapier.com/storage/photos/5d2d774d7a330ef9c2e5494e5fcda169.png)

Funny enough, the `span_type` for the `trace()` looks correct:

https://github.com/DataDog/dd-trace-py/blob/7a3d0f69610a160bfd71cec8caa8366bc6977a8e/ddtrace/contrib/django/cache.py#L55

Seems to be working fine for db:

https://github.com/DataDog/dd-trace-py/blob/7a3d0f69610a160bfd71cec8caa8366bc6977a8e/ddtrace/contrib/django/db.py#L98


Discussion:
@palazzem definitely appreciate your eyes on this!
Looks like this might be a Datadog UI issue -- as if you drill into a trace it understands there are cache calls. Is it because the service has a mixed span types?

![](https://cdn.zapier.com/storage/photos/a95b3de152f30660f278145b1b8430e8.png)
Yeah, a little more digging and the likely fix is to have a custom `service=...` similar to how the database patching works, or at least a way to override (IE: maybe `getattr(backend, '_datadog_service')` or something). 
Hello @bryanhelmig ! actually the Django cache is not reported as a standalone service, so you will not find it as "Cache" in the main service list. As you found, the Span will be considered as a cache. 

Maybe a solution could be changing the name via Django settings (ref to ours: https://github.com/DataDog/dd-trace-py/blob/master/ddtrace/contrib/django/conf.py#L24-L38), so that by default the cache is not considered a standalone service, but you can change it like you will do for the Database service.

Do you think it could solve your problem?
My understanding is changing the name in the settings will impact all of the services reported, including http?
Hey @bryanhelmig ! just to be clear, currently we don't offer this option in the Django settings. Mine was mostly a thought to see if it could address your problem. It means:
* we add this feature to change the name of the caching layer
* the caching layer is reported as a standalone service (so you'll see a "Cache" service in the service list)
* you can filter and see stats for the Caching layer

`http` services (so Django itself or `requests` or other libraries) will not be impacted by the change.

What do you think?
Yeah, I think those all make sense -- thanks for the clarification!
Perfect! I'll enqueue this change in another bugfix release because we've already many things on going (most important are `ddtrace-run` fix and the new Celery integration). As soon the next release is out, we can work on this immediately.
Awesome! If we can get pymemcached support -- we might prefer that over this! It'll sort of solve both our problems! 😄 
Perfect! `pymemcache` support is already merged (https://github.com/DataDog/dd-trace-py/pull/511) and scheduled for release (0.13.0). I'm in touch with @apolloFER to test it in the meantime.

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
@@ -51,6 +51,8 @@
   tracer. Usually this configuration must be updated with a meaningful name.
 * ``DEFAULT_DATABASE_PREFIX`` (default: ``''``): set a prefix value to database services,
   so that your service is listed such as `prefix-defaultdb`.
+* ``DEFAULT_CACHE_SERVICE`` (default: ``''``): set the django cache service name used
+  by the tracer. Change this name if you want to see django cache spans as a cache application.
 * ``TAGS`` (default: ``{}``): set global tags that should be applied to all
   spans.
 * ``TRACER`` (default: ``ddtrace.tracer``): set the default tracer
diff --git a/ddtrace/contrib/django/cache.py b/ddtrace/contrib/django/cache.py
--- a/ddtrace/contrib/django/cache.py
+++ b/ddtrace/contrib/django/cache.py
@@ -47,12 +47,15 @@ def _trace_operation(fn, method_name):
         """
         Return a wrapped function that traces a cache operation
         """
+        cache_service_name = settings.DEFAULT_CACHE_SERVICE \
+            if settings.DEFAULT_CACHE_SERVICE else settings.DEFAULT_SERVICE
+
         @wraps(fn)
         def wrapped(self, *args, **kwargs):
             # get the original function method
             method = getattr(self, DATADOG_NAMESPACE.format(method=method_name))
             with tracer.trace('django.cache',
-                    span_type=TYPE, service=settings.DEFAULT_SERVICE) as span:
+                    span_type=TYPE, service=cache_service_name) as span:
                 # update the resource name and tag the cache backend
                 span.resource = _resource_from_cache_prefix(method_name, self)
                 cache_backend = '{}.{}'.format(self.__module__, self.__class__.__name__)
diff --git a/ddtrace/contrib/django/conf.py b/ddtrace/contrib/django/conf.py
--- a/ddtrace/contrib/django/conf.py
+++ b/ddtrace/contrib/django/conf.py
@@ -31,6 +31,7 @@
     'INSTRUMENT_TEMPLATE': True,
     'DEFAULT_DATABASE_PREFIX': '',
     'DEFAULT_SERVICE': 'django',
+    'DEFAULT_CACHE_SERVICE': '',
     'ENABLED': True,
     'DISTRIBUTED_TRACING': False,
     'TAGS': {},


Test patch:
diff --git a/tests/contrib/django/test_cache_client.py b/tests/contrib/django/test_cache_client.py
--- a/tests/contrib/django/test_cache_client.py
+++ b/tests/contrib/django/test_cache_client.py
@@ -42,6 +42,21 @@ def test_cache_get(self):
         assert_dict_issuperset(span.meta, expected_meta)
         assert start < span.start < span.start + span.duration < end
 
+    @override_ddtrace_settings(DEFAULT_CACHE_SERVICE='foo')
+    def test_cache_service_can_be_overriden(self):
+        # get the default cache
+        cache = caches['default']
+
+        # (trace) the cache miss
+        hit = cache.get('missing_key')
+
+        # tests
+        spans = self.tracer.writer.pop()
+        eq_(len(spans), 1)
+
+        span = spans[0]
+        eq_(span.service, 'foo')
+
     @override_ddtrace_settings(INSTRUMENT_CACHE=False)
     def test_cache_disabled(self):
         # get the default cache