Files
2020-04-08 10:39:44 -07:00

47 lines
2.0 KiB
Python

import ddtrace
def _wrap_submit(func, instance, args, kwargs):
"""
Wrap `Executor` method used to submit a work executed in another
thread. This wrapper ensures that a new `Context` is created and
properly propagated using an intermediate function.
"""
# If there isn't a currently active context, then do not create one
# DEV: Calling `.active()` when there isn't an active context will create a new context
# DEV: We need to do this in case they are either:
# - Starting nested futures
# - Starting futures from outside of an existing context
#
# In either of these cases we essentially will propagate the wrong context between futures
#
# The resolution is to not create/propagate a new context if one does not exist, but let the
# future's thread create the context instead.
current_ctx = None
if ddtrace.tracer.context_provider._has_active_context():
current_ctx = ddtrace.tracer.context_provider.active()
# If we have a context then make sure we clone it
# DEV: We don't know if the future will finish executing before the parent span finishes
# so we clone to ensure we properly collect/report the future's spans
current_ctx = current_ctx.clone()
# extract the target function that must be executed in
# a new thread and the `target` arguments
fn = args[0]
fn_args = args[1:]
return func(_wrap_execution, current_ctx, fn, fn_args, kwargs)
def _wrap_execution(ctx, fn, args, kwargs):
"""
Intermediate target function that is executed in a new thread;
it receives the original function with arguments and keyword
arguments, including our tracing `Context`. The current context
provider sets the Active context in a thread local storage
variable because it's outside the asynchronous loop.
"""
if ctx is not None:
ddtrace.tracer.context_provider.activate(ctx)
return fn(*args, **kwargs)