Repository: DataDog/dd-trace-py

Issue:
calling get_correlation_ids exception occurs
I just setup injecting `trace_id` and `span_id` into a custom logger and it works well for most logs however in certain circumstances (I think for example a request that throws an exception right away) the function `get_correlation_ids` throw an exception:

```
AttributeError: 'NoneType' object has no attribute 'get_current_span'
return self.get_call_context().get_current_span()
File "/usr/local/lib/python3.7/site-packages/ddtrace/tracer.py", line 310, in current_span
span = tracer.current_span()
File "/usr/local/lib/python3.7/site-packages/ddtrace/helpers.py", line 34, in get_correlation_ids
trace_id, span_id = helpers.get_correlation_ids()
```

I wonder if this is because in `get_correlation_ids` [it always assumes the the global `tracer` is initialized and not `None`](https://github.com/DataDog/dd-trace-py/blob/master/ddtrace/helpers.py#L28). That's the only way to generate this exception from within `get_correlation_ids`. So my question is, is that possible and something that just wasn't accounted for and should be checked?

Using dd-trace-py version `0.22.0`



Discussion:

Resolving patch:
diff --git a/ddtrace/helpers.py b/ddtrace/helpers.py
--- a/ddtrace/helpers.py
+++ b/ddtrace/helpers.py
@@ -32,6 +32,6 @@ def get_correlation_ids(tracer=None):
         return None, None
 
     span = tracer.current_span()
-    if span is None:
+    if not span:
         return None, None
     return span.trace_id, span.span_id
diff --git a/ddtrace/tracer.py b/ddtrace/tracer.py
--- a/ddtrace/tracer.py
+++ b/ddtrace/tracer.py
@@ -300,14 +300,20 @@ def current_root_span(self):
             # set the host just once on the root span
             root_span.set_tag('host', '127.0.0.1')
         """
-        return self.get_call_context().get_current_root_span()
+        ctx = self.get_call_context()
+        if ctx:
+            return ctx.get_current_root_span()
+        return None
 
     def current_span(self):
         """
         Return the active span for the current call context or ``None``
         if no spans are available.
         """
-        return self.get_call_context().get_current_span()
+        ctx = self.get_call_context()
+        if ctx:
+            return ctx.get_current_span()
+        return None
 
     def record(self, context):
         """


Test patch:
diff --git a/tests/test_helpers.py b/tests/test_helpers.py
--- a/tests/test_helpers.py
+++ b/tests/test_helpers.py
@@ -1,6 +1,6 @@
-from ddtrace import helpers
+import mock
 
-from nose.tools import eq_, ok_
+from ddtrace import helpers
 
 from .base import BaseTracerTestCase
 from .util import override_global_tracer
@@ -16,16 +16,16 @@ def test_correlation_identifiers(self):
             active_trace_id, active_span_id = span.trace_id, span.span_id
             trace_id, span_id = helpers.get_correlation_ids()
 
-        eq_(trace_id, active_trace_id)
-        eq_(span_id, active_span_id)
+        self.assertEqual(trace_id, active_trace_id)
+        self.assertEqual(span_id, active_span_id)
 
     def test_correlation_identifiers_without_trace(self):
         # ensures `None` is returned if no Traces are active
         with override_global_tracer(self.tracer):
             trace_id, span_id = helpers.get_correlation_ids()
 
-        ok_(trace_id is None)
-        ok_(span_id is None)
+        self.assertIsNone(trace_id)
+        self.assertIsNone(span_id)
 
     def test_correlation_identifiers_with_disabled_trace(self):
         # ensures `None` is returned if tracer is disabled
@@ -34,5 +34,15 @@ def test_correlation_identifiers_with_disabled_trace(self):
             self.tracer.trace('MockSpan')
             trace_id, span_id = helpers.get_correlation_ids()
 
-        ok_(trace_id is None)
-        ok_(span_id is None)
+        self.assertIsNone(trace_id)
+        self.assertIsNone(span_id)
+
+    def test_correlation_identifiers_missing_context(self):
+        # ensures we return `None` if there is no current context
+        self.tracer.get_call_context = mock.MagicMock(return_value=None)
+
+        with override_global_tracer(self.tracer):
+            trace_id, span_id = helpers.get_correlation_ids()
+
+        self.assertIsNone(trace_id)
+        self.assertIsNone(span_id)
diff --git a/tests/test_tracer.py b/tests/test_tracer.py
--- a/tests/test_tracer.py
+++ b/tests/test_tracer.py
@@ -329,6 +329,12 @@ def test_tracer_current_span(self):
         span = self.trace('fake_span')
         self.assertEqual(self.tracer.current_span(), span)
 
+    def test_tracer_current_span_missing_context(self):
+        self.assertIsNone(self.tracer.current_span())
+
+    def test_tracer_current_root_span_missing_context(self):
+        self.assertIsNone(self.tracer.current_root_span())
+
     def test_default_provider_get(self):
         # Tracer Context Provider must return a Context object
         # even if empty