We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
logging in `ddtrace.context.Context` causes deadlock
### Summary
`Context.close_span` logs out any unfinished child spans when the parent span closes. if log injection is enabled, the patched logger [tries to get the current span](https://github.com/DataDog/dd-trace-py/blob/682f9d9fdf8a5ad4285cc6adc31e8177599f5d0a/ddtrace/contrib/logging/patch.py#L38) which [tries to grab the same lock](https://github.com/DataDog/dd-trace-py/blob/682f9d9fdf8a5ad4285cc6adc31e8177599f5d0a/ddtrace/context.py#L92-L100) that `Context.close_span` is already holding.

https://github.com/DataDog/dd-trace-py/blob/682f9d9fdf8a5ad4285cc6adc31e8177599f5d0a/ddtrace/context.py#L125-L147

### Which version of dd-trace-py are you using?
`0.36.0`

### How can we reproduce your problem?
run `DATADOG_TRACE_DEBUG=true DD_LOGS_INJECTION=true ddtrace-run python script.py`
```py
# script.py
import ddtrace

def main():
    with ddtrace.tracer.trace("main") as span:
        unfinished_span = ddtrace.tracer.trace("unfinished_span")
        # unfinished_span.finish()  # <-- if this runs, deadlock will not occur

if __name__ == "__main__":
    main()
```

### What is the result that you get?
script deadlocks and hangs

### What is the result that you expected?
unfinished spans don't deadlock code

### Notes
possibly similar to https://github.com/DataDog/dd-trace-py/issues/1003

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `ddtrace/context.py`
Content:
```
1 import logging
2 import threading
3 
4 from .constants import HOSTNAME_KEY, SAMPLING_PRIORITY_KEY, ORIGIN_KEY
5 from .internal.logger import get_logger
6 from .internal import hostname
7 from .settings import config
8 from .utils.formats import asbool, get_env
9 
10 log = get_logger(__name__)
11 
12 
13 class Context(object):
14     """
15     Context is used to keep track of a hierarchy of spans for the current
16     execution flow. During each logical execution, the same ``Context`` is
17     used to represent a single logical trace, even if the trace is built
18     asynchronously.
19 
20     A single code execution may use multiple ``Context`` if part of the execution
21     must not be related to the current tracing. As example, a delayed job may
22     compose a standalone trace instead of being related to the same trace that
23     generates the job itself. On the other hand, if it's part of the same
24     ``Context``, it will be related to the original trace.
25 
26     This data structure is thread-safe.
27     """
28     _partial_flush_enabled = asbool(get_env('tracer', 'partial_flush_enabled', default=False))
29     _partial_flush_min_spans = int(get_env('tracer', 'partial_flush_min_spans', default=500))
30 
31     def __init__(self, trace_id=None, span_id=None, sampling_priority=None, _dd_origin=None):
32         """
33         Initialize a new thread-safe ``Context``.
34 
35         :param int trace_id: trace_id of parent span
36         :param int span_id: span_id of parent span
37         """
38         self._trace = []
39         self._finished_spans = 0
40         self._current_span = None
41         self._lock = threading.Lock()
42 
43         self._parent_trace_id = trace_id
44         self._parent_span_id = span_id
45         self._sampling_priority = sampling_priority
46         self._dd_origin = _dd_origin
47 
48     @property
49     def trace_id(self):
50         """Return current context trace_id."""
51         with self._lock:
52             return self._parent_trace_id
53 
54     @property
55     def span_id(self):
56         """Return current context span_id."""
57         with self._lock:
58             return self._parent_span_id
59 
60     @property
61     def sampling_priority(self):
62         """Return current context sampling priority."""
63         with self._lock:
64             return self._sampling_priority
65 
66     @sampling_priority.setter
67     def sampling_priority(self, value):
68         """Set sampling priority."""
69         with self._lock:
70             self._sampling_priority = value
71 
72     def clone(self):
73         """
74         Partially clones the current context.
75         It copies everything EXCEPT the registered and finished spans.
76         """
77         with self._lock:
78             new_ctx = Context(
79                 trace_id=self._parent_trace_id,
80                 span_id=self._parent_span_id,
81                 sampling_priority=self._sampling_priority,
82             )
83             new_ctx._current_span = self._current_span
84             return new_ctx
85 
86     def get_current_root_span(self):
87         """
88         Return the root span of the context or None if it does not exist.
89         """
90         return self._trace[0] if len(self._trace) > 0 else None
91 
92     def get_current_span(self):
93         """
94         Return the last active span that corresponds to the last inserted
95         item in the trace list. This cannot be considered as the current active
96         span in asynchronous environments, because some spans can be closed
97         earlier while child spans still need to finish their traced execution.
98         """
99         with self._lock:
100             return self._current_span
101 
102     def _set_current_span(self, span):
103         """
104         Set current span internally.
105 
106         Non-safe if not used with a lock. For internal Context usage only.
107         """
108         self._current_span = span
109         if span:
110             self._parent_trace_id = span.trace_id
111             self._parent_span_id = span.span_id
112         else:
113             self._parent_span_id = None
114 
115     def add_span(self, span):
116         """
117         Add a span to the context trace list, keeping it as the last active span.
118         """
119         with self._lock:
120             self._set_current_span(span)
121 
122             self._trace.append(span)
123             span._context = self
124 
125     def close_span(self, span):
126         """
127         Mark a span as a finished, increasing the internal counter to prevent
128         cycles inside _trace list.
129         """
130         with self._lock:
131             self._finished_spans += 1
132             self._set_current_span(span._parent)
133 
134             # notify if the trace is not closed properly; this check is executed only
135             # if the debug logging is enabled and when the root span is closed
136             # for an unfinished trace. This logging is meant to be used for debugging
137             # reasons, and it doesn't mean that the trace is wrongly generated.
138             # In asynchronous environments, it's legit to close the root span before
139             # some children. On the other hand, asynchronous web frameworks still expect
140             # to close the root span after all the children.
141             if span.tracer and span.tracer.log.isEnabledFor(logging.DEBUG) and span._parent is None:
142                 unfinished_spans = [x for x in self._trace if not x.finished]
143                 if unfinished_spans:
144                     log.debug('Root span "%s" closed, but the trace has %d unfinished spans:',
145                               span.name, len(unfinished_spans))
146                     for wrong_span in unfinished_spans:
147                         log.debug('\n%s', wrong_span.pprint())
148 
149     def _is_sampled(self):
150         return any(span.sampled for span in self._trace)
151 
152     def get(self):
153         """
154         Returns a tuple containing the trace list generated in the current context and
155         if the context is sampled or not. It returns (None, None) if the ``Context`` is
156         not finished. If a trace is returned, the ``Context`` will be reset so that it
157         can be re-used immediately.
158 
159         This operation is thread-safe.
160         """
161         with self._lock:
162             # All spans are finished?
163             if self._finished_spans == len(self._trace):
164                 # get the trace
165                 trace = self._trace
166                 sampled = self._is_sampled()
167                 sampling_priority = self._sampling_priority
168                 # attach the sampling priority to the context root span
169                 if sampled and sampling_priority is not None and trace:
170                     trace[0].set_metric(SAMPLING_PRIORITY_KEY, sampling_priority)
171                 origin = self._dd_origin
172                 # attach the origin to the root span tag
173                 if sampled and origin is not None and trace:
174                     trace[0].set_tag(ORIGIN_KEY, origin)
175 
176                 # Set hostname tag if they requested it
177                 if config.report_hostname:
178                     # DEV: `get_hostname()` value is cached
179                     trace[0].set_tag(HOSTNAME_KEY, hostname.get_hostname())
180 
181                 # clean the current state
182                 self._trace = []
183                 self._finished_spans = 0
184                 self._parent_trace_id = None
185                 self._parent_span_id = None
186                 self._sampling_priority = None
187                 return trace, sampled
188 
189             elif self._partial_flush_enabled:
190                 finished_spans = [t for t in self._trace if t.finished]
191                 if len(finished_spans) >= self._partial_flush_min_spans:
192                     # partial flush when enabled and we have more than the minimal required spans
193                     trace = self._trace
194                     sampled = self._is_sampled()
195                     sampling_priority = self._sampling_priority
196                     # attach the sampling priority to the context root span
197                     if sampled and sampling_priority is not None and trace:
198                         trace[0].set_metric(SAMPLING_PRIORITY_KEY, sampling_priority)
199                     origin = self._dd_origin
200                     # attach the origin to the root span tag
201                     if sampled and origin is not None and trace:
202                         trace[0].set_tag(ORIGIN_KEY, origin)
203 
204                     # Set hostname tag if they requested it
205                     if config.report_hostname:
206                         # DEV: `get_hostname()` value is cached
207                         trace[0].set_tag(HOSTNAME_KEY, hostname.get_hostname())
208 
209                     self._finished_spans = 0
210 
211                     # Any open spans will remain as `self._trace`
212                     # Any finished spans will get returned to be flushed
213                     self._trace = [t for t in self._trace if not t.finished]
214 
215                     return finished_spans, sampled
216             return None, None
217
```

Path: `ddtrace/constants.py`
Content:
```
1 FILTERS_KEY = 'FILTERS'
2 SAMPLE_RATE_METRIC_KEY = '_sample_rate'
3 SAMPLING_PRIORITY_KEY = '_sampling_priority_v1'
4 ANALYTICS_SAMPLE_RATE_KEY = '_dd1.sr.eausr'
5 SAMPLING_AGENT_DECISION = '_dd.agent_psr'
6 SAMPLING_RULE_DECISION = '_dd.rule_psr'
7 SAMPLING_LIMIT_DECISION = '_dd.limit_psr'
8 ORIGIN_KEY = '_dd.origin'
9 HOSTNAME_KEY = '_dd.hostname'
10 ENV_KEY = 'env'
11 VERSION_KEY = 'version'
12 SERVICE_KEY = 'service.name'
13 SERVICE_VERSION_KEY = 'service.version'
14 SPAN_MEASURED_KEY = '_dd.measured'
15 
16 NUMERIC_TAGS = (ANALYTICS_SAMPLE_RATE_KEY, )
17 
18 MANUAL_DROP_KEY = 'manual.drop'
19 MANUAL_KEEP_KEY = 'manual.keep'
20
```

Path: `ddtrace/contrib/logging/patch.py`
Content:
```
1 import logging
2 
3 import ddtrace
4 
5 from ...utils.wrappers import unwrap as _u
6 from ...vendor.wrapt import wrap_function_wrapper as _w
7 
8 RECORD_ATTR_TRACE_ID = "dd.trace_id"
9 RECORD_ATTR_SPAN_ID = "dd.span_id"
10 RECORD_ATTR_ENV = "dd.env"
11 RECORD_ATTR_VERSION = "dd.version"
12 RECORD_ATTR_SERVICE = "dd.service"
13 RECORD_ATTR_VALUE_ZERO = 0
14 RECORD_ATTR_VALUE_EMPTY = ""
15 
16 ddtrace.config._add("logging", dict(tracer=None,))  # by default, override here for custom tracer
17 
18 
19 def _get_current_span(tracer=None):
20     """Helper to get the currently active span"""
21     if not tracer:
22         tracer = ddtrace.tracer
23 
24     if not tracer.enabled:
25         return None
26 
27     return tracer.current_span()
28 
29 
30 def _w_makeRecord(func, instance, args, kwargs):
31     # Get the LogRecord instance for this log
32     record = func(*args, **kwargs)
33 
34     setattr(record, RECORD_ATTR_VERSION, ddtrace.config.version or "")
35     setattr(record, RECORD_ATTR_ENV, ddtrace.config.env or "")
36     setattr(record, RECORD_ATTR_SERVICE, ddtrace.config.service or "")
37 
38     span = _get_current_span(tracer=ddtrace.config.logging.tracer)
39     if span:
40         setattr(record, RECORD_ATTR_TRACE_ID, span.trace_id)
41         setattr(record, RECORD_ATTR_SPAN_ID, span.span_id)
42     else:
43         setattr(record, RECORD_ATTR_TRACE_ID, RECORD_ATTR_VALUE_ZERO)
44         setattr(record, RECORD_ATTR_SPAN_ID, RECORD_ATTR_VALUE_ZERO)
45 
46     return record
47 
48 
49 def patch():
50     """
51     Patch ``logging`` module in the Python Standard Library for injection of
52     tracer information by wrapping the base factory method ``Logger.makeRecord``
53     """
54     if getattr(logging, "_datadog_patch", False):
55         return
56     setattr(logging, "_datadog_patch", True)
57 
58     _w(logging.Logger, "makeRecord", _w_makeRecord)
59 
60 
61 def unpatch():
62     if getattr(logging, "_datadog_patch", False):
63         setattr(logging, "_datadog_patch", False)
64 
65         _u(logging.Logger, "makeRecord")
66
```
--- 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/ddtrace/constants.py b/ddtrace/constants.py
--- a/ddtrace/constants.py
+++ b/ddtrace/constants.py
@@ -17,3 +17,5 @@
 
 MANUAL_DROP_KEY = 'manual.drop'
 MANUAL_KEEP_KEY = 'manual.keep'
+
+LOG_SPAN_KEY = '__datadog_log_span'
diff --git a/ddtrace/context.py b/ddtrace/context.py
--- a/ddtrace/context.py
+++ b/ddtrace/context.py
@@ -1,7 +1,7 @@
 import logging
 import threading
 
-from .constants import HOSTNAME_KEY, SAMPLING_PRIORITY_KEY, ORIGIN_KEY
+from .constants import HOSTNAME_KEY, SAMPLING_PRIORITY_KEY, ORIGIN_KEY, LOG_SPAN_KEY
 from .internal.logger import get_logger
 from .internal import hostname
 from .settings import config
@@ -139,12 +139,13 @@
             # some children. On the other hand, asynchronous web frameworks still expect
             # to close the root span after all the children.
             if span.tracer and span.tracer.log.isEnabledFor(logging.DEBUG) and span._parent is None:
+                extra = {LOG_SPAN_KEY: span}
                 unfinished_spans = [x for x in self._trace if not x.finished]
                 if unfinished_spans:
                     log.debug('Root span "%s" closed, but the trace has %d unfinished spans:',
-                              span.name, len(unfinished_spans))
+                              span.name, len(unfinished_spans), extra=extra)
                     for wrong_span in unfinished_spans:
-                        log.debug('\n%s', wrong_span.pprint())
+                        log.debug('\n%s', wrong_span.pprint(), extra=extra)
 
     def _is_sampled(self):
         return any(span.sampled for span in self._trace)
diff --git a/ddtrace/contrib/logging/patch.py b/ddtrace/contrib/logging/patch.py
--- a/ddtrace/contrib/logging/patch.py
+++ b/ddtrace/contrib/logging/patch.py
@@ -35,7 +35,14 @@
     setattr(record, RECORD_ATTR_ENV, ddtrace.config.env or "")
     setattr(record, RECORD_ATTR_SERVICE, ddtrace.config.service or "")
 
-    span = _get_current_span(tracer=ddtrace.config.logging.tracer)
+    # logs from internal logger may explicitly pass the current span to
+    # avoid deadlocks in getting the current span while already in locked code.
+    span_from_log = getattr(record, ddtrace.constants.LOG_SPAN_KEY, None)
+    if isinstance(span_from_log, ddtrace.Span):
+        span = span_from_log
+    else:
+        span = _get_current_span(tracer=ddtrace.config.logging.tracer)
+
     if span:
         setattr(record, RECORD_ATTR_TRACE_ID, span.trace_id)
         setattr(record, RECORD_ATTR_SPAN_ID, span.span_id)

{"golden_diff": "diff --git a/ddtrace/constants.py b/ddtrace/constants.py\n--- a/ddtrace/constants.py\n+++ b/ddtrace/constants.py\n@@ -17,3 +17,5 @@\n \n MANUAL_DROP_KEY = 'manual.drop'\n MANUAL_KEEP_KEY = 'manual.keep'\n+\n+LOG_SPAN_KEY = '__datadog_log_span'\ndiff --git a/ddtrace/context.py b/ddtrace/context.py\n--- a/ddtrace/context.py\n+++ b/ddtrace/context.py\n@@ -1,7 +1,7 @@\n import logging\n import threading\n \n-from .constants import HOSTNAME_KEY, SAMPLING_PRIORITY_KEY, ORIGIN_KEY\n+from .constants import HOSTNAME_KEY, SAMPLING_PRIORITY_KEY, ORIGIN_KEY, LOG_SPAN_KEY\n from .internal.logger import get_logger\n from .internal import hostname\n from .settings import config\n@@ -139,12 +139,13 @@\n             # some children. On the other hand, asynchronous web frameworks still expect\n             # to close the root span after all the children.\n             if span.tracer and span.tracer.log.isEnabledFor(logging.DEBUG) and span._parent is None:\n+                extra = {LOG_SPAN_KEY: span}\n                 unfinished_spans = [x for x in self._trace if not x.finished]\n                 if unfinished_spans:\n                     log.debug('Root span \"%s\" closed, but the trace has %d unfinished spans:',\n-                              span.name, len(unfinished_spans))\n+                              span.name, len(unfinished_spans), extra=extra)\n                     for wrong_span in unfinished_spans:\n-                        log.debug('\\n%s', wrong_span.pprint())\n+                        log.debug('\\n%s', wrong_span.pprint(), extra=extra)\n \n     def _is_sampled(self):\n         return any(span.sampled for span in self._trace)\ndiff --git a/ddtrace/contrib/logging/patch.py b/ddtrace/contrib/logging/patch.py\n--- a/ddtrace/contrib/logging/patch.py\n+++ b/ddtrace/contrib/logging/patch.py\n@@ -35,7 +35,14 @@\n     setattr(record, RECORD_ATTR_ENV, ddtrace.config.env or \"\")\n     setattr(record, RECORD_ATTR_SERVICE, ddtrace.config.service or \"\")\n \n-    span = _get_current_span(tracer=ddtrace.config.logging.tracer)\n+    # logs from internal logger may explicitly pass the current span to\n+    # avoid deadlocks in getting the current span while already in locked code.\n+    span_from_log = getattr(record, ddtrace.constants.LOG_SPAN_KEY, None)\n+    if isinstance(span_from_log, ddtrace.Span):\n+        span = span_from_log\n+    else:\n+        span = _get_current_span(tracer=ddtrace.config.logging.tracer)\n+\n     if span:\n         setattr(record, RECORD_ATTR_TRACE_ID, span.trace_id)\n         setattr(record, RECORD_ATTR_SPAN_ID, span.span_id)\n", "issue": "logging in `ddtrace.context.Context` causes deadlock\n### Summary\r\n`Context.close_span` logs out any unfinished child spans when the parent span closes. if log injection is enabled, the patched logger [tries to get the current span](https://github.com/DataDog/dd-trace-py/blob/682f9d9fdf8a5ad4285cc6adc31e8177599f5d0a/ddtrace/contrib/logging/patch.py#L38) which [tries to grab the same lock](https://github.com/DataDog/dd-trace-py/blob/682f9d9fdf8a5ad4285cc6adc31e8177599f5d0a/ddtrace/context.py#L92-L100) that `Context.close_span` is already holding.\r\n\r\nhttps://github.com/DataDog/dd-trace-py/blob/682f9d9fdf8a5ad4285cc6adc31e8177599f5d0a/ddtrace/context.py#L125-L147\r\n\r\n### Which version of dd-trace-py are you using?\r\n`0.36.0`\r\n\r\n### How can we reproduce your problem?\r\nrun `DATADOG_TRACE_DEBUG=true DD_LOGS_INJECTION=true ddtrace-run python script.py`\r\n```py\r\n# script.py\r\nimport ddtrace\r\n\r\ndef main():\r\n    with ddtrace.tracer.trace(\"main\") as span:\r\n        unfinished_span = ddtrace.tracer.trace(\"unfinished_span\")\r\n        # unfinished_span.finish()  # <-- if this runs, deadlock will not occur\r\n\r\nif __name__ == \"__main__\":\r\n    main()\r\n```\r\n\r\n### What is the result that you get?\r\nscript deadlocks and hangs\r\n\r\n### What is the result that you expected?\r\nunfinished spans don't deadlock code\r\n\r\n### Notes\r\npossibly similar to https://github.com/DataDog/dd-trace-py/issues/1003\n", "before_files": [{"content": "import logging\nimport threading\n\nfrom .constants import HOSTNAME_KEY, SAMPLING_PRIORITY_KEY, ORIGIN_KEY\nfrom .internal.logger import get_logger\nfrom .internal import hostname\nfrom .settings import config\nfrom .utils.formats import asbool, get_env\n\nlog = get_logger(__name__)\n\n\nclass Context(object):\n    \"\"\"\n    Context is used to keep track of a hierarchy of spans for the current\n    execution flow. During each logical execution, the same ``Context`` is\n    used to represent a single logical trace, even if the trace is built\n    asynchronously.\n\n    A single code execution may use multiple ``Context`` if part of the execution\n    must not be related to the current tracing. As example, a delayed job may\n    compose a standalone trace instead of being related to the same trace that\n    generates the job itself. On the other hand, if it's part of the same\n    ``Context``, it will be related to the original trace.\n\n    This data structure is thread-safe.\n    \"\"\"\n    _partial_flush_enabled = asbool(get_env('tracer', 'partial_flush_enabled', default=False))\n    _partial_flush_min_spans = int(get_env('tracer', 'partial_flush_min_spans', default=500))\n\n    def __init__(self, trace_id=None, span_id=None, sampling_priority=None, _dd_origin=None):\n        \"\"\"\n        Initialize a new thread-safe ``Context``.\n\n        :param int trace_id: trace_id of parent span\n        :param int span_id: span_id of parent span\n        \"\"\"\n        self._trace = []\n        self._finished_spans = 0\n        self._current_span = None\n        self._lock = threading.Lock()\n\n        self._parent_trace_id = trace_id\n        self._parent_span_id = span_id\n        self._sampling_priority = sampling_priority\n        self._dd_origin = _dd_origin\n\n    @property\n    def trace_id(self):\n        \"\"\"Return current context trace_id.\"\"\"\n        with self._lock:\n            return self._parent_trace_id\n\n    @property\n    def span_id(self):\n        \"\"\"Return current context span_id.\"\"\"\n        with self._lock:\n            return self._parent_span_id\n\n    @property\n    def sampling_priority(self):\n        \"\"\"Return current context sampling priority.\"\"\"\n        with self._lock:\n            return self._sampling_priority\n\n    @sampling_priority.setter\n    def sampling_priority(self, value):\n        \"\"\"Set sampling priority.\"\"\"\n        with self._lock:\n            self._sampling_priority = value\n\n    def clone(self):\n        \"\"\"\n        Partially clones the current context.\n        It copies everything EXCEPT the registered and finished spans.\n        \"\"\"\n        with self._lock:\n            new_ctx = Context(\n                trace_id=self._parent_trace_id,\n                span_id=self._parent_span_id,\n                sampling_priority=self._sampling_priority,\n            )\n            new_ctx._current_span = self._current_span\n            return new_ctx\n\n    def get_current_root_span(self):\n        \"\"\"\n        Return the root span of the context or None if it does not exist.\n        \"\"\"\n        return self._trace[0] if len(self._trace) > 0 else None\n\n    def get_current_span(self):\n        \"\"\"\n        Return the last active span that corresponds to the last inserted\n        item in the trace list. This cannot be considered as the current active\n        span in asynchronous environments, because some spans can be closed\n        earlier while child spans still need to finish their traced execution.\n        \"\"\"\n        with self._lock:\n            return self._current_span\n\n    def _set_current_span(self, span):\n        \"\"\"\n        Set current span internally.\n\n        Non-safe if not used with a lock. For internal Context usage only.\n        \"\"\"\n        self._current_span = span\n        if span:\n            self._parent_trace_id = span.trace_id\n            self._parent_span_id = span.span_id\n        else:\n            self._parent_span_id = None\n\n    def add_span(self, span):\n        \"\"\"\n        Add a span to the context trace list, keeping it as the last active span.\n        \"\"\"\n        with self._lock:\n            self._set_current_span(span)\n\n            self._trace.append(span)\n            span._context = self\n\n    def close_span(self, span):\n        \"\"\"\n        Mark a span as a finished, increasing the internal counter to prevent\n        cycles inside _trace list.\n        \"\"\"\n        with self._lock:\n            self._finished_spans += 1\n            self._set_current_span(span._parent)\n\n            # notify if the trace is not closed properly; this check is executed only\n            # if the debug logging is enabled and when the root span is closed\n            # for an unfinished trace. This logging is meant to be used for debugging\n            # reasons, and it doesn't mean that the trace is wrongly generated.\n            # In asynchronous environments, it's legit to close the root span before\n            # some children. On the other hand, asynchronous web frameworks still expect\n            # to close the root span after all the children.\n            if span.tracer and span.tracer.log.isEnabledFor(logging.DEBUG) and span._parent is None:\n                unfinished_spans = [x for x in self._trace if not x.finished]\n                if unfinished_spans:\n                    log.debug('Root span \"%s\" closed, but the trace has %d unfinished spans:',\n                              span.name, len(unfinished_spans))\n                    for wrong_span in unfinished_spans:\n                        log.debug('\\n%s', wrong_span.pprint())\n\n    def _is_sampled(self):\n        return any(span.sampled for span in self._trace)\n\n    def get(self):\n        \"\"\"\n        Returns a tuple containing the trace list generated in the current context and\n        if the context is sampled or not. It returns (None, None) if the ``Context`` is\n        not finished. If a trace is returned, the ``Context`` will be reset so that it\n        can be re-used immediately.\n\n        This operation is thread-safe.\n        \"\"\"\n        with self._lock:\n            # All spans are finished?\n            if self._finished_spans == len(self._trace):\n                # get the trace\n                trace = self._trace\n                sampled = self._is_sampled()\n                sampling_priority = self._sampling_priority\n                # attach the sampling priority to the context root span\n                if sampled and sampling_priority is not None and trace:\n                    trace[0].set_metric(SAMPLING_PRIORITY_KEY, sampling_priority)\n                origin = self._dd_origin\n                # attach the origin to the root span tag\n                if sampled and origin is not None and trace:\n                    trace[0].set_tag(ORIGIN_KEY, origin)\n\n                # Set hostname tag if they requested it\n                if config.report_hostname:\n                    # DEV: `get_hostname()` value is cached\n                    trace[0].set_tag(HOSTNAME_KEY, hostname.get_hostname())\n\n                # clean the current state\n                self._trace = []\n                self._finished_spans = 0\n                self._parent_trace_id = None\n                self._parent_span_id = None\n                self._sampling_priority = None\n                return trace, sampled\n\n            elif self._partial_flush_enabled:\n                finished_spans = [t for t in self._trace if t.finished]\n                if len(finished_spans) >= self._partial_flush_min_spans:\n                    # partial flush when enabled and we have more than the minimal required spans\n                    trace = self._trace\n                    sampled = self._is_sampled()\n                    sampling_priority = self._sampling_priority\n                    # attach the sampling priority to the context root span\n                    if sampled and sampling_priority is not None and trace:\n                        trace[0].set_metric(SAMPLING_PRIORITY_KEY, sampling_priority)\n                    origin = self._dd_origin\n                    # attach the origin to the root span tag\n                    if sampled and origin is not None and trace:\n                        trace[0].set_tag(ORIGIN_KEY, origin)\n\n                    # Set hostname tag if they requested it\n                    if config.report_hostname:\n                        # DEV: `get_hostname()` value is cached\n                        trace[0].set_tag(HOSTNAME_KEY, hostname.get_hostname())\n\n                    self._finished_spans = 0\n\n                    # Any open spans will remain as `self._trace`\n                    # Any finished spans will get returned to be flushed\n                    self._trace = [t for t in self._trace if not t.finished]\n\n                    return finished_spans, sampled\n            return None, None\n", "path": "ddtrace/context.py"}, {"content": "FILTERS_KEY = 'FILTERS'\nSAMPLE_RATE_METRIC_KEY = '_sample_rate'\nSAMPLING_PRIORITY_KEY = '_sampling_priority_v1'\nANALYTICS_SAMPLE_RATE_KEY = '_dd1.sr.eausr'\nSAMPLING_AGENT_DECISION = '_dd.agent_psr'\nSAMPLING_RULE_DECISION = '_dd.rule_psr'\nSAMPLING_LIMIT_DECISION = '_dd.limit_psr'\nORIGIN_KEY = '_dd.origin'\nHOSTNAME_KEY = '_dd.hostname'\nENV_KEY = 'env'\nVERSION_KEY = 'version'\nSERVICE_KEY = 'service.name'\nSERVICE_VERSION_KEY = 'service.version'\nSPAN_MEASURED_KEY = '_dd.measured'\n\nNUMERIC_TAGS = (ANALYTICS_SAMPLE_RATE_KEY, )\n\nMANUAL_DROP_KEY = 'manual.drop'\nMANUAL_KEEP_KEY = 'manual.keep'\n", "path": "ddtrace/constants.py"}, {"content": "import logging\n\nimport ddtrace\n\nfrom ...utils.wrappers import unwrap as _u\nfrom ...vendor.wrapt import wrap_function_wrapper as _w\n\nRECORD_ATTR_TRACE_ID = \"dd.trace_id\"\nRECORD_ATTR_SPAN_ID = \"dd.span_id\"\nRECORD_ATTR_ENV = \"dd.env\"\nRECORD_ATTR_VERSION = \"dd.version\"\nRECORD_ATTR_SERVICE = \"dd.service\"\nRECORD_ATTR_VALUE_ZERO = 0\nRECORD_ATTR_VALUE_EMPTY = \"\"\n\nddtrace.config._add(\"logging\", dict(tracer=None,))  # by default, override here for custom tracer\n\n\ndef _get_current_span(tracer=None):\n    \"\"\"Helper to get the currently active span\"\"\"\n    if not tracer:\n        tracer = ddtrace.tracer\n\n    if not tracer.enabled:\n        return None\n\n    return tracer.current_span()\n\n\ndef _w_makeRecord(func, instance, args, kwargs):\n    # Get the LogRecord instance for this log\n    record = func(*args, **kwargs)\n\n    setattr(record, RECORD_ATTR_VERSION, ddtrace.config.version or \"\")\n    setattr(record, RECORD_ATTR_ENV, ddtrace.config.env or \"\")\n    setattr(record, RECORD_ATTR_SERVICE, ddtrace.config.service or \"\")\n\n    span = _get_current_span(tracer=ddtrace.config.logging.tracer)\n    if span:\n        setattr(record, RECORD_ATTR_TRACE_ID, span.trace_id)\n        setattr(record, RECORD_ATTR_SPAN_ID, span.span_id)\n    else:\n        setattr(record, RECORD_ATTR_TRACE_ID, RECORD_ATTR_VALUE_ZERO)\n        setattr(record, RECORD_ATTR_SPAN_ID, RECORD_ATTR_VALUE_ZERO)\n\n    return record\n\n\ndef patch():\n    \"\"\"\n    Patch ``logging`` module in the Python Standard Library for injection of\n    tracer information by wrapping the base factory method ``Logger.makeRecord``\n    \"\"\"\n    if getattr(logging, \"_datadog_patch\", False):\n        return\n    setattr(logging, \"_datadog_patch\", True)\n\n    _w(logging.Logger, \"makeRecord\", _w_makeRecord)\n\n\ndef unpatch():\n    if getattr(logging, \"_datadog_patch\", False):\n        setattr(logging, \"_datadog_patch\", False)\n\n        _u(logging.Logger, \"makeRecord\")\n", "path": "ddtrace/contrib/logging/patch.py"}], "after_files": [{"content": "import logging\nimport threading\n\nfrom .constants import HOSTNAME_KEY, SAMPLING_PRIORITY_KEY, ORIGIN_KEY, LOG_SPAN_KEY\nfrom .internal.logger import get_logger\nfrom .internal import hostname\nfrom .settings import config\nfrom .utils.formats import asbool, get_env\n\nlog = get_logger(__name__)\n\n\nclass Context(object):\n    \"\"\"\n    Context is used to keep track of a hierarchy of spans for the current\n    execution flow. During each logical execution, the same ``Context`` is\n    used to represent a single logical trace, even if the trace is built\n    asynchronously.\n\n    A single code execution may use multiple ``Context`` if part of the execution\n    must not be related to the current tracing. As example, a delayed job may\n    compose a standalone trace instead of being related to the same trace that\n    generates the job itself. On the other hand, if it's part of the same\n    ``Context``, it will be related to the original trace.\n\n    This data structure is thread-safe.\n    \"\"\"\n    _partial_flush_enabled = asbool(get_env('tracer', 'partial_flush_enabled', default=False))\n    _partial_flush_min_spans = int(get_env('tracer', 'partial_flush_min_spans', default=500))\n\n    def __init__(self, trace_id=None, span_id=None, sampling_priority=None, _dd_origin=None):\n        \"\"\"\n        Initialize a new thread-safe ``Context``.\n\n        :param int trace_id: trace_id of parent span\n        :param int span_id: span_id of parent span\n        \"\"\"\n        self._trace = []\n        self._finished_spans = 0\n        self._current_span = None\n        self._lock = threading.Lock()\n\n        self._parent_trace_id = trace_id\n        self._parent_span_id = span_id\n        self._sampling_priority = sampling_priority\n        self._dd_origin = _dd_origin\n\n    @property\n    def trace_id(self):\n        \"\"\"Return current context trace_id.\"\"\"\n        with self._lock:\n            return self._parent_trace_id\n\n    @property\n    def span_id(self):\n        \"\"\"Return current context span_id.\"\"\"\n        with self._lock:\n            return self._parent_span_id\n\n    @property\n    def sampling_priority(self):\n        \"\"\"Return current context sampling priority.\"\"\"\n        with self._lock:\n            return self._sampling_priority\n\n    @sampling_priority.setter\n    def sampling_priority(self, value):\n        \"\"\"Set sampling priority.\"\"\"\n        with self._lock:\n            self._sampling_priority = value\n\n    def clone(self):\n        \"\"\"\n        Partially clones the current context.\n        It copies everything EXCEPT the registered and finished spans.\n        \"\"\"\n        with self._lock:\n            new_ctx = Context(\n                trace_id=self._parent_trace_id,\n                span_id=self._parent_span_id,\n                sampling_priority=self._sampling_priority,\n            )\n            new_ctx._current_span = self._current_span\n            return new_ctx\n\n    def get_current_root_span(self):\n        \"\"\"\n        Return the root span of the context or None if it does not exist.\n        \"\"\"\n        return self._trace[0] if len(self._trace) > 0 else None\n\n    def get_current_span(self):\n        \"\"\"\n        Return the last active span that corresponds to the last inserted\n        item in the trace list. This cannot be considered as the current active\n        span in asynchronous environments, because some spans can be closed\n        earlier while child spans still need to finish their traced execution.\n        \"\"\"\n        with self._lock:\n            return self._current_span\n\n    def _set_current_span(self, span):\n        \"\"\"\n        Set current span internally.\n\n        Non-safe if not used with a lock. For internal Context usage only.\n        \"\"\"\n        self._current_span = span\n        if span:\n            self._parent_trace_id = span.trace_id\n            self._parent_span_id = span.span_id\n        else:\n            self._parent_span_id = None\n\n    def add_span(self, span):\n        \"\"\"\n        Add a span to the context trace list, keeping it as the last active span.\n        \"\"\"\n        with self._lock:\n            self._set_current_span(span)\n\n            self._trace.append(span)\n            span._context = self\n\n    def close_span(self, span):\n        \"\"\"\n        Mark a span as a finished, increasing the internal counter to prevent\n        cycles inside _trace list.\n        \"\"\"\n        with self._lock:\n            self._finished_spans += 1\n            self._set_current_span(span._parent)\n\n            # notify if the trace is not closed properly; this check is executed only\n            # if the debug logging is enabled and when the root span is closed\n            # for an unfinished trace. This logging is meant to be used for debugging\n            # reasons, and it doesn't mean that the trace is wrongly generated.\n            # In asynchronous environments, it's legit to close the root span before\n            # some children. On the other hand, asynchronous web frameworks still expect\n            # to close the root span after all the children.\n            if span.tracer and span.tracer.log.isEnabledFor(logging.DEBUG) and span._parent is None:\n                extra = {LOG_SPAN_KEY: span}\n                unfinished_spans = [x for x in self._trace if not x.finished]\n                if unfinished_spans:\n                    log.debug('Root span \"%s\" closed, but the trace has %d unfinished spans:',\n                              span.name, len(unfinished_spans), extra=extra)\n                    for wrong_span in unfinished_spans:\n                        log.debug('\\n%s', wrong_span.pprint(), extra=extra)\n\n    def _is_sampled(self):\n        return any(span.sampled for span in self._trace)\n\n    def get(self):\n        \"\"\"\n        Returns a tuple containing the trace list generated in the current context and\n        if the context is sampled or not. It returns (None, None) if the ``Context`` is\n        not finished. If a trace is returned, the ``Context`` will be reset so that it\n        can be re-used immediately.\n\n        This operation is thread-safe.\n        \"\"\"\n        with self._lock:\n            # All spans are finished?\n            if self._finished_spans == len(self._trace):\n                # get the trace\n                trace = self._trace\n                sampled = self._is_sampled()\n                sampling_priority = self._sampling_priority\n                # attach the sampling priority to the context root span\n                if sampled and sampling_priority is not None and trace:\n                    trace[0].set_metric(SAMPLING_PRIORITY_KEY, sampling_priority)\n                origin = self._dd_origin\n                # attach the origin to the root span tag\n                if sampled and origin is not None and trace:\n                    trace[0].set_tag(ORIGIN_KEY, origin)\n\n                # Set hostname tag if they requested it\n                if config.report_hostname:\n                    # DEV: `get_hostname()` value is cached\n                    trace[0].set_tag(HOSTNAME_KEY, hostname.get_hostname())\n\n                # clean the current state\n                self._trace = []\n                self._finished_spans = 0\n                self._parent_trace_id = None\n                self._parent_span_id = None\n                self._sampling_priority = None\n                return trace, sampled\n\n            elif self._partial_flush_enabled:\n                finished_spans = [t for t in self._trace if t.finished]\n                if len(finished_spans) >= self._partial_flush_min_spans:\n                    # partial flush when enabled and we have more than the minimal required spans\n                    trace = self._trace\n                    sampled = self._is_sampled()\n                    sampling_priority = self._sampling_priority\n                    # attach the sampling priority to the context root span\n                    if sampled and sampling_priority is not None and trace:\n                        trace[0].set_metric(SAMPLING_PRIORITY_KEY, sampling_priority)\n                    origin = self._dd_origin\n                    # attach the origin to the root span tag\n                    if sampled and origin is not None and trace:\n                        trace[0].set_tag(ORIGIN_KEY, origin)\n\n                    # Set hostname tag if they requested it\n                    if config.report_hostname:\n                        # DEV: `get_hostname()` value is cached\n                        trace[0].set_tag(HOSTNAME_KEY, hostname.get_hostname())\n\n                    self._finished_spans = 0\n\n                    # Any open spans will remain as `self._trace`\n                    # Any finished spans will get returned to be flushed\n                    self._trace = [t for t in self._trace if not t.finished]\n\n                    return finished_spans, sampled\n            return None, None\n", "path": "ddtrace/context.py"}, {"content": "FILTERS_KEY = 'FILTERS'\nSAMPLE_RATE_METRIC_KEY = '_sample_rate'\nSAMPLING_PRIORITY_KEY = '_sampling_priority_v1'\nANALYTICS_SAMPLE_RATE_KEY = '_dd1.sr.eausr'\nSAMPLING_AGENT_DECISION = '_dd.agent_psr'\nSAMPLING_RULE_DECISION = '_dd.rule_psr'\nSAMPLING_LIMIT_DECISION = '_dd.limit_psr'\nORIGIN_KEY = '_dd.origin'\nHOSTNAME_KEY = '_dd.hostname'\nENV_KEY = 'env'\nVERSION_KEY = 'version'\nSERVICE_KEY = 'service.name'\nSERVICE_VERSION_KEY = 'service.version'\nSPAN_MEASURED_KEY = '_dd.measured'\n\nNUMERIC_TAGS = (ANALYTICS_SAMPLE_RATE_KEY, )\n\nMANUAL_DROP_KEY = 'manual.drop'\nMANUAL_KEEP_KEY = 'manual.keep'\n\nLOG_SPAN_KEY = '__datadog_log_span'\n", "path": "ddtrace/constants.py"}, {"content": "import logging\n\nimport ddtrace\n\nfrom ...utils.wrappers import unwrap as _u\nfrom ...vendor.wrapt import wrap_function_wrapper as _w\n\nRECORD_ATTR_TRACE_ID = \"dd.trace_id\"\nRECORD_ATTR_SPAN_ID = \"dd.span_id\"\nRECORD_ATTR_ENV = \"dd.env\"\nRECORD_ATTR_VERSION = \"dd.version\"\nRECORD_ATTR_SERVICE = \"dd.service\"\nRECORD_ATTR_VALUE_ZERO = 0\nRECORD_ATTR_VALUE_EMPTY = \"\"\n\nddtrace.config._add(\"logging\", dict(tracer=None,))  # by default, override here for custom tracer\n\n\ndef _get_current_span(tracer=None):\n    \"\"\"Helper to get the currently active span\"\"\"\n    if not tracer:\n        tracer = ddtrace.tracer\n\n    if not tracer.enabled:\n        return None\n\n    return tracer.current_span()\n\n\ndef _w_makeRecord(func, instance, args, kwargs):\n    # Get the LogRecord instance for this log\n    record = func(*args, **kwargs)\n\n    setattr(record, RECORD_ATTR_VERSION, ddtrace.config.version or \"\")\n    setattr(record, RECORD_ATTR_ENV, ddtrace.config.env or \"\")\n    setattr(record, RECORD_ATTR_SERVICE, ddtrace.config.service or \"\")\n\n    # logs from internal logger may explicitly pass the current span to\n    # avoid deadlocks in getting the current span while already in locked code.\n    span_from_log = getattr(record, ddtrace.constants.LOG_SPAN_KEY, None)\n    if isinstance(span_from_log, ddtrace.Span):\n        span = span_from_log\n    else:\n        span = _get_current_span(tracer=ddtrace.config.logging.tracer)\n\n    if span:\n        setattr(record, RECORD_ATTR_TRACE_ID, span.trace_id)\n        setattr(record, RECORD_ATTR_SPAN_ID, span.span_id)\n    else:\n        setattr(record, RECORD_ATTR_TRACE_ID, RECORD_ATTR_VALUE_ZERO)\n        setattr(record, RECORD_ATTR_SPAN_ID, RECORD_ATTR_VALUE_ZERO)\n\n    return record\n\n\ndef patch():\n    \"\"\"\n    Patch ``logging`` module in the Python Standard Library for injection of\n    tracer information by wrapping the base factory method ``Logger.makeRecord``\n    \"\"\"\n    if getattr(logging, \"_datadog_patch\", False):\n        return\n    setattr(logging, \"_datadog_patch\", True)\n\n    _w(logging.Logger, \"makeRecord\", _w_makeRecord)\n\n\ndef unpatch():\n    if getattr(logging, \"_datadog_patch\", False):\n        setattr(logging, \"_datadog_patch\", False)\n\n        _u(logging.Logger, \"makeRecord\")\n", "path": "ddtrace/contrib/logging/patch.py"}]}