Skip to content

Commit

Permalink
SDK Tracer treats invalid span parent like null (#233, #235)
Browse files Browse the repository at this point in the history
The SDK tracer will now create spans with invalid parents
as brand new spans, similar to not having a parent at all.

Adding this behavior to the Tracer ensures that integrations do not have
to handle invalid span contexts in their own code, and ensures that behavior
is consistent with w3c tracecontext (which specifies invalid results should
be handled by creating new spans).

Setting the parent to none on spans if the parent context is invalid, reducing logic
to handle that situation in downstream processing like exporters.
  • Loading branch information
toumorokoshi committed Oct 24, 2019
1 parent 5c89850 commit 26d56c0
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 23 deletions.
50 changes: 27 additions & 23 deletions opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -376,30 +376,33 @@ def create_span(
as a child of the current span in this tracer's context, or as a root
span if no current span exists.
"""
span_id = generate_span_id()

if parent is Tracer.CURRENT_SPAN:
parent = self.get_current_span()

if parent is None:
parent_context = None
new_span_context = trace_api.SpanContext(
generate_trace_id(), generate_span_id()
)
parent_context = parent
if isinstance(parent_context, trace_api.Span):
parent_context = parent.get_context()

if parent_context is not None and not isinstance(
parent_context, trace_api.SpanContext
):
raise TypeError

if parent_context is None or not parent_context.is_valid():
parent = parent_context = None
trace_id = generate_trace_id()
trace_options = None
trace_state = None
else:
if isinstance(parent, trace_api.Span):
parent_context = parent.get_context()
elif isinstance(parent, trace_api.SpanContext):
parent_context = parent
else:
# TODO: error handling
raise TypeError

new_span_context = trace_api.SpanContext(
parent_context.trace_id,
generate_span_id(),
parent_context.trace_options,
parent_context.trace_state,
)
trace_id = parent_context.trace_id
trace_options = parent_context.trace_options
trace_state = parent_context.trace_state

context = trace_api.SpanContext(
trace_id, span_id, trace_options, trace_state
)

# The sampler decides whether to create a real or no-op span at the
# time of span creation. No-op spans do not record events, and are not
Expand All @@ -408,23 +411,24 @@ def create_span(
# to include information about the sampling decision.
sampling_decision = self.sampler.should_sample(
parent_context,
new_span_context.trace_id,
new_span_context.span_id,
context.trace_id,
context.span_id,
name,
{}, # TODO: links
)

if sampling_decision.sampled:
return Span(
name=name,
context=new_span_context,
context=context,
parent=parent,
sampler=self.sampler,
attributes=sampling_decision.attributes,
span_processor=self._active_span_processor,
kind=kind,
)
return trace_api.DefaultSpan(context=new_span_context)

return trace_api.DefaultSpan(context=context)

@contextmanager
def use_span(
Expand Down
14 changes: 14 additions & 0 deletions opentelemetry-sdk/tests/trace/test_trace.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,20 @@ def test_sampler_no_sampling(self):


class TestSpanCreation(unittest.TestCase):
def test_create_span_invalid_spancontext(self):
"""If an invalid span context is passed as the parent, the created
span should use a new span id.
Invalid span contexts should also not be added as a parent. This
eliminates redundant error handling logic in exporters.
"""
tracer = trace.Tracer("test_create_span_invalid_spancontext")
new_span = tracer.create_span(
"root", parent=trace_api.INVALID_SPAN_CONTEXT
)
self.assertTrue(new_span.context.is_valid())
self.assertIsNone(new_span.parent)

def test_start_span_implicit(self):
tracer = trace.Tracer("test_start_span_implicit")

Expand Down

0 comments on commit 26d56c0

Please sign in to comment.