Repository: DataDog/dd-trace-py

Issue:
bug: log correlation does not work when a valid Context is active before a Span is created


### Which version of dd-trace-py are you using?

`0.59.1`

### Which version of pip are you using?

`22.0.4`

### Which version of the libraries are you using?

```
attrs==21.4.0
ddtrace==0.59.1
packaging==21.3
protobuf==3.19.4
pyparsing==3.0.7
six==1.16.0
tenacity==8.0.1
```

### How can we reproduce your problem?

```python
from threading import Thread

import ddtrace
from ddtrace import tracer

def print_ddtrace_info():
    print("current_span", tracer.current_span())
    print("current context", tracer.current_trace_context())
    print("log context", tracer.get_log_correlation_context())

def t(context):
    print("----thread start------")
    print(context)
    print("-----thread before activate-----")
    print_ddtrace_info()
    print("----------")
    tracer.context_provider.activate(context)
    print("-----thread after activate-----")
    print_ddtrace_info()
    print("---thread end-------")


if __name__ == '__main__':
    ddtrace.patch_all()
    print("-- main --")
    print_ddtrace_info()
    print("----------")
    with tracer.trace("main"):
        print("-- main context manager--")
        print_ddtrace_info()
        print("----------")
        Thread(target=t, args=(tracer.current_trace_context(),)).start()

```

### What is the result that you get?

```
-- main --
current_span None
current context None
log context {'trace_id': '0', 'span_id': '0', 'service': '', 'version': '', 'env': ''}
----------
-- main context manager--
current_span <Span(id=12483312343277968584,trace_id=8304735650546411141,parent_id=None,name=main)>
current context Context(trace_id=8304735650546411141, span_id=12483312343277968584, _meta={}, _metrics={'_sampling_priority_v1': 1})
log context {'trace_id': '8304735650546411141', 'span_id': '12483312343277968584', 'service': '', 'version': '', 'env': ''}
----------
----thread start------
Context(trace_id=8304735650546411141, span_id=12483312343277968584, _meta={}, _metrics={'_sampling_priority_v1': 1})
-----thread before activate-----
current_span None
current context None
log context {'trace_id': '0', 'span_id': '0', 'service': '', 'version': '', 'env': ''}
----------
-----thread after activate-----
current_span None
current context Context(trace_id=8304735650546411141, span_id=12483312343277968584, _meta={}, _metrics={'_sampling_priority_v1': 1})
log context {'trace_id': '0', 'span_id': '0', 'service': '', 'version': '', 'env': ''}
---thread end-------
```

### What is the result that you expected?

I expect that after activating context in the thread I can see same `trace_id` and `span_id`  in log correlation context as in main thread, so in this case:
```
log context {'trace_id': '8304735650546411141', 'span_id': '12483312343277968584', 'service': '', 'version': '', 'env': ''}
```

but I get
```
log context {'trace_id': '0', 'span_id': '0', 'service': '', 'version': '', 'env': ''}
```




Discussion:
I was following this documentation: https://ddtrace.readthedocs.io/en/stable/advanced_usage.html#tracing-across-threads
@hoou thank you for opening this issue. We discussed it briefly today and we agree with you on the expected behavior.

the log correlation code was explicitly written to only use the currently active span in the thread, but given your example/use case where we have an active context, the log correlation should still work.

I don't have an ETA on us getting to work on this just yet, but I wanted to give you an update that we agree that a change can be made.

Resolving patch:
diff --git a/ddtrace/tracer.py b/ddtrace/tracer.py
--- a/ddtrace/tracer.py
+++ b/ddtrace/tracer.py
@@ -268,18 +268,18 @@ def get_log_correlation_context(self):
         span id of the current active span, as well as the configured service, version, and environment names.
         If there is no active span, a dictionary with an empty string for each value will be returned.
         """
-        span = None
+        active = None  # type: Optional[Union[Context, Span]]
         if self.enabled:
-            span = self.current_span()
+            active = self.context_provider.active()
 
-        if span and span.service:
-            service = span.service
+        if isinstance(active, Span) and active.service:
+            service = active.service
         else:
             service = config.service
 
         return {
-            "trace_id": str(span.trace_id) if span else "0",
-            "span_id": str(span.span_id) if span else "0",
+            "trace_id": str(active.trace_id) if active else "0",
+            "span_id": str(active.span_id) if active else "0",
             "service": service or "",
             "version": config.version or "",
             "env": config.env or "",


Test patch:
diff --git a/tests/tracer/test_correlation_log_context.py b/tests/tracer/test_correlation_log_context.py
--- a/tests/tracer/test_correlation_log_context.py
+++ b/tests/tracer/test_correlation_log_context.py
@@ -4,6 +4,7 @@
 from ddtrace import Tracer
 from ddtrace import config
 from ddtrace import tracer
+from ddtrace.context import Context
 from ddtrace.opentracer.tracer import Tracer as OT_Tracer
 from tests.utils import override_global_config
 
@@ -71,6 +72,20 @@ def test_get_log_correlation_context(self, global_config):
             "version": "test-version",
         }
 
+        tracer.context_provider.activate(
+            Context(
+                span_id=234,
+                trace_id=4321,
+            )
+        )
+        assert test_tracer.get_log_correlation_context() == {
+            "span_id": "234",
+            "trace_id": "4321",
+            "service": "test-service",
+            "env": "test-env",
+            "version": "test-version",
+        }
+
     def test_get_log_correlation_context_opentracer(self, global_config):
         """Ensure expected DDLogRecord generated via get_correlation_log_record with an opentracing Tracer."""
         ot_tracer = OT_Tracer(service_name="test-service")