Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parent is now always passed in via Context, intead of Span or SpanContext #1146

Merged
merged 25 commits into from
Oct 8, 2020
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -118,14 +118,13 @@ def _trace_prerun(self, *args, **kwargs):
return

request = task.request
tracectx = propagators.extract(carrier_extractor, request) or {}
parent = get_current_span(tracectx)
tracectx = propagators.extract(carrier_extractor, request) or None

logger.debug("prerun signal start task_id=%s", task_id)

operation_name = "{0}/{1}".format(_TASK_RUN, task.name)
span = self._tracer.start_span(
operation_name, parent=parent, kind=trace.SpanKind.CONSUMER
operation_name, parent=tracectx, kind=trace.SpanKind.CONSUMER
)

activation = self._tracer.use_span(span, end_on_exit=True)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -630,6 +630,10 @@ def start_span(
# Use the specified parent or the active span if possible. Otherwise,
# use a `None` parent, which triggers the creation of a new trace.
parent = child_of.unwrap() if child_of else None
if isinstance(parent, OtelSpanContext):
parent = DefaultSpan(parent)

parent_context = set_span_in_context(parent)

links = []
if references:
Expand All @@ -645,7 +649,7 @@ def start_span(

span = self._otel_tracer.start_span(
operation_name,
parent,
parent=parent_context,
links=links,
attributes=tags,
start_time=start_time_ns,
Expand Down
2 changes: 2 additions & 0 deletions opentelemetry-api/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
([#1194](https://github.com/open-telemetry/opentelemetry-python/pull/1194))
- Make instances of SpanContext immutable
([#1134](https://github.com/open-telemetry/opentelemetry-python/pull/1134))
- Parent is now always passed in via Context, intead of Span or SpanContext
([#1146](https://github.com/open-telemetry/opentelemetry-python/pull/1146))

## Version 0.13b0

Expand Down
24 changes: 12 additions & 12 deletions opentelemetry-api/src/opentelemetry/trace/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
from contextlib import contextmanager
from logging import getLogger

from opentelemetry.context.context import Context
from opentelemetry.trace.ids_generator import IdsGenerator, RandomIdsGenerator
from opentelemetry.trace.propagation import (
get_current_span,
Expand All @@ -102,9 +103,6 @@

logger = getLogger(__name__)

# TODO: quarantine
ParentSpan = typing.Optional[typing.Union["Span", "SpanContext"]]


class LinkBase(abc.ABC):
def __init__(self, context: "SpanContext") -> None:
Expand Down Expand Up @@ -228,7 +226,7 @@ class Tracer(abc.ABC):
def start_span(
self,
name: str,
parent: ParentSpan = CURRENT_SPAN,
parent: typing.Optional[Context] = None,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if the variable is worth renaming. "parent" was vague to begin with, and if you look at any descriptions of how tracing works, one would immediately think "parent" refers to the span (since you specifically call the span a child of the parent span).

This would also be useful to rename since new API users will get exceptions when their existing code passes an argument as parent. Rather than get a new slightly confusing error ("why is the API now rejecting this previously valid value?"), they will get an argument not supported error, and look up the docs to see the variable changed.

parent_context is the first thing that comes to mind, but I worry a user would get it confused with the parent SpanContext.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@toumorokoshi yeah I had a similar thought around the name. Looking at some of the other implementations, it appears parent and parent_context are the current names used. I'm happy to change it to parent_context as it's at least a bit more meaningful than parent. The alternative i can think of would be to just call it context

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either sounds great!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd say context is the better name. A Context object passed in this argument may or may not contain a parent Span, making the name parent_context less accurate and context_with_or_without_the_parent_span is a bit long.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the feedback, went with context.

kind: SpanKind = SpanKind.INTERNAL,
attributes: types.Attributes = None,
links: typing.Sequence[Link] = (),
Expand All @@ -242,8 +240,9 @@ def start_span(
method, see :meth:`start_as_current_span`.

By default the current span in the context will be used as parent, but an
explicit parent can also be specified, either a `Span` or a `opentelemetry.trace.SpanContext`.
If the specified value is `None`, the created span will be a root span.
explicit parent can also be specified, by passing in a `Context` containing
a current `Span`. If there is no current span in the global `Context` or in
the specified parent `Context`, the created span will be a root span.

The span can be used as a context manager. On exiting the context manager,
the span's end() method will be called.
Expand All @@ -257,7 +256,8 @@ def start_span(

Args:
name: The name of the span to be created.
parent: The span's parent. Defaults to the current span.
parent: An optional Context containing the span's parent. Defaults to the
global context.
kind: The span's kind (relationship to parent). Note that is
meaningful even if there is no parent.
attributes: The span's attributes.
Expand All @@ -278,7 +278,7 @@ def start_span(
def start_as_current_span(
self,
name: str,
parent: ParentSpan = CURRENT_SPAN,
parent: typing.Optional[Context] = None,
kind: SpanKind = SpanKind.INTERNAL,
attributes: types.Attributes = None,
links: typing.Sequence[Link] = (),
Expand Down Expand Up @@ -315,7 +315,8 @@ def start_as_current_span(

Args:
name: The name of the span to be created.
parent: The span's parent. Defaults to the current span.
parent: An optional Context containing the span's parent. Defaults to the
global context.
kind: The span's kind (relationship to parent). Note that is
meaningful even if there is no parent.
attributes: The span's attributes.
Expand Down Expand Up @@ -357,7 +358,7 @@ class DefaultTracer(Tracer):
def start_span(
self,
name: str,
parent: ParentSpan = Tracer.CURRENT_SPAN,
parent: typing.Optional[Context] = None,
kind: SpanKind = SpanKind.INTERNAL,
attributes: types.Attributes = None,
links: typing.Sequence[Link] = (),
Expand All @@ -371,7 +372,7 @@ def start_span(
def start_as_current_span(
self,
name: str,
parent: ParentSpan = Tracer.CURRENT_SPAN,
parent: typing.Optional[Context] = None,
kind: SpanKind = SpanKind.INTERNAL,
attributes: types.Attributes = None,
links: typing.Sequence[Link] = (),
Expand Down Expand Up @@ -447,7 +448,6 @@ def get_tracer_provider() -> TracerProvider:
"DefaultTracerProvider",
"Link",
"LinkBase",
"ParentSpan",
"RandomIdsGenerator",
"Span",
"SpanContext",
Expand Down
2 changes: 2 additions & 0 deletions opentelemetry-sdk/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
([#1153](https://github.com/open-telemetry/opentelemetry-python/pull/1153))
- Renaming metrics Batcher to Processor
([#1203](https://github.com/open-telemetry/opentelemetry-python/pull/1203))
- Parent is now always passed in via Context, intead of Span or SpanContext
([#1146](https://github.com/open-telemetry/opentelemetry-python/pull/1146))

## Version 0.13b0

Expand Down
16 changes: 6 additions & 10 deletions opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ class Span(trace_api.Span):
name: The name of the operation this span represents
context: The immutable span context
parent: This span's parent's `opentelemetry.trace.SpanContext`, or
codeboten marked this conversation as resolved.
Show resolved Hide resolved
null if this is a root span
None if this is a root span
sampler: The sampler used to create this span
trace_config: TODO
resource: Entity producing telemetry
Expand Down Expand Up @@ -683,7 +683,7 @@ def __init__(
def start_as_current_span(
self,
name: str,
parent: trace_api.ParentSpan = trace_api.Tracer.CURRENT_SPAN,
parent: Optional[context_api.Context] = None,
kind: trace_api.SpanKind = trace_api.SpanKind.INTERNAL,
attributes: types.Attributes = None,
links: Sequence[trace_api.Link] = (),
Expand All @@ -694,27 +694,23 @@ def start_as_current_span(
def start_span( # pylint: disable=too-many-locals
self,
name: str,
parent: trace_api.ParentSpan = trace_api.Tracer.CURRENT_SPAN,
parent: Optional[context_api.Context] = None,
kind: trace_api.SpanKind = trace_api.SpanKind.INTERNAL,
attributes: types.Attributes = None,
links: Sequence[trace_api.Link] = (),
start_time: Optional[int] = None,
set_status_on_exception: bool = True,
) -> trace_api.Span:
if parent is Tracer.CURRENT_SPAN:
parent = trace_api.get_current_span()

parent_context = parent
if isinstance(parent_context, trace_api.Span):
parent_context = parent.get_context()
parent_context = trace_api.get_current_span(parent).get_context()
Copy link
Contributor

@lzchen lzchen Oct 6, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that there is the concept of a Context being passed into span start, I'm wondering if it is valuable to rename span.get_context() to span.get_span_context() to avoid confusion. I know that the Context isn't actually a PART of Span, it probably is only here (during start and use) that it would be confusing. Thoughts?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And maybe even renaming the variable from parent_context to parent_span_context.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would +1 on "get_span_context", although with open-telemetry/opentelemetry-specification#1033 that might not even be necessary.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I renamed get_context to get_span_context and parent_context to parent_span_context where it made sense. I tested the water with renaming context in the Span to span_context and it turned out to be pretty messy. Not sure if it's worth the rename on that one. Thoughts @lzchen & @toumorokoshi?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's fine, I don't know if I have a strong opinion on the attribute name on the span.

And I'm completely comfortable with taking these PRs piecemeal too.


if parent_context is not None and not isinstance(
parent_context, trace_api.SpanContext
):
raise TypeError("parent must be a Span, SpanContext or None.")
raise TypeError("parent_context must be a SpanContext or None.")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this type validation is still valuable. But practically speaking, this would only be an issue if someone directly manipulates the context, and doesn't use the API functions.

Is that a common enough case to even catch here?

Alternatively I suppose this is cheap enough.


if parent_context is None or not parent_context.is_valid:
parent = parent_context = None
parent_context = None
trace_id = self.source.ids_generator.generate_trace_id()
trace_flags = None
trace_state = None
Expand Down
62 changes: 39 additions & 23 deletions opentelemetry-sdk/tests/trace/test_trace.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,9 @@ def test_default_sampler(self):
# Check that the default tracer creates real spans via the default
# sampler
root_span = tracer.start_span(name="root span", parent=None)
ctx = trace_api.set_span_in_context(root_span)
self.assertIsInstance(root_span, trace.Span)
child_span = tracer.start_span(name="child span", parent=root_span)
child_span = tracer.start_span(name="child span", parent=ctx)
self.assertIsInstance(child_span, trace.Span)
self.assertTrue(root_span.context.trace_flags.sampled)
self.assertEqual(
Expand All @@ -154,8 +155,9 @@ def test_sampler_no_sampling(self):
# Check that the default tracer creates no-op spans if the sampler
# decides not to sampler
root_span = tracer.start_span(name="root span", parent=None)
ctx = trace_api.set_span_in_context(root_span)
self.assertIsInstance(root_span, trace_api.DefaultSpan)
child_span = tracer.start_span(name="child span", parent=root_span)
child_span = tracer.start_span(name="child span", parent=ctx)
self.assertIsInstance(child_span, trace_api.DefaultSpan)
self.assertEqual(
root_span.get_context().trace_flags, trace_api.TraceFlags.DEFAULT
Expand All @@ -174,9 +176,10 @@ def test_start_span_invalid_spancontext(self):
eliminates redundant error handling logic in exporters.
"""
tracer = new_tracer()
new_span = tracer.start_span(
"root", parent=trace_api.INVALID_SPAN_CONTEXT
parent_context = trace_api.set_span_in_context(
trace_api.INVALID_SPAN_CONTEXT
)
new_span = tracer.start_span("root", parent=parent_context)
self.assertTrue(new_span.context.is_valid)
self.assertIsNone(new_span.parent)

Expand Down Expand Up @@ -282,13 +285,18 @@ def test_start_span_implicit(self):
def test_start_span_explicit(self):
tracer = new_tracer()

other_parent = trace_api.SpanContext(
trace_id=0x000000000000000000000000DEADBEEF,
span_id=0x00000000DEADBEF0,
is_remote=False,
trace_flags=trace_api.TraceFlags(trace_api.TraceFlags.SAMPLED),
other_parent = trace.Span(
"name",
trace_api.SpanContext(
trace_id=0x000000000000000000000000DEADBEEF,
span_id=0x00000000DEADBEF0,
is_remote=False,
trace_flags=trace_api.TraceFlags(trace_api.TraceFlags.SAMPLED),
),
)

other_parent_context = trace_api.set_span_in_context(other_parent)

self.assertEqual(trace_api.get_current_span(), trace_api.INVALID_SPAN)

root = tracer.start_span("root")
Expand All @@ -299,27 +307,31 @@ def test_start_span_explicit(self):
with tracer.use_span(root, True):
self.assertIs(trace_api.get_current_span(), root)

with tracer.start_span("stepchild", other_parent) as child:
with tracer.start_span("stepchild", other_parent_context) as child:
# The child's parent should be the one passed in,
# not the current span.
self.assertNotEqual(child.parent, root)
self.assertIs(child.parent, other_parent)
self.assertIs(child.parent, other_parent.get_context())

self.assertIsNotNone(child.start_time)
self.assertIsNone(child.end_time)

# The child should inherit its context from the explicit
# parent, not the current span.
child_context = child.get_context()
self.assertEqual(other_parent.trace_id, child_context.trace_id)
self.assertEqual(
other_parent.get_context().trace_id, child_context.trace_id
)
self.assertNotEqual(
other_parent.span_id, child_context.span_id
other_parent.get_context().span_id, child_context.span_id
)
self.assertEqual(
other_parent.trace_state, child_context.trace_state
other_parent.get_context().trace_state,
child_context.trace_state,
)
self.assertEqual(
other_parent.trace_flags, child_context.trace_flags
other_parent.get_context().trace_flags,
child_context.trace_flags,
)

# Verify start_span() did not set the current span.
Expand Down Expand Up @@ -352,12 +364,16 @@ def test_start_as_current_span_implicit(self):
def test_start_as_current_span_explicit(self):
tracer = new_tracer()

other_parent = trace_api.SpanContext(
trace_id=0x000000000000000000000000DEADBEEF,
span_id=0x00000000DEADBEF0,
is_remote=False,
trace_flags=trace_api.TraceFlags(trace_api.TraceFlags.SAMPLED),
other_parent = trace.Span(
"name",
trace_api.SpanContext(
trace_id=0x000000000000000000000000DEADBEEF,
span_id=0x00000000DEADBEF0,
is_remote=False,
trace_flags=trace_api.TraceFlags(trace_api.TraceFlags.SAMPLED),
),
)
other_parent_ctx = trace_api.set_span_in_context(other_parent)

self.assertEqual(trace_api.get_current_span(), trace_api.INVALID_SPAN)

Expand All @@ -369,14 +385,14 @@ def test_start_as_current_span_explicit(self):
self.assertIsNone(root.end_time)

with tracer.start_as_current_span(
"stepchild", other_parent
"stepchild", other_parent_ctx
) as child:
# The child should become the current span as usual, but its
# parent should be the one passed in, not the
# previously-current span.
self.assertIs(trace_api.get_current_span(), child)
self.assertNotEqual(child.parent, root)
self.assertIs(child.parent, other_parent)
self.assertIs(child.parent, other_parent.get_context())

# After exiting the child's scope the last span on the stack should
# become current, not the child's parent.
Expand Down Expand Up @@ -538,7 +554,7 @@ def test_sampling_attributes(self):
"attr-in-both": "decision-attr",
}
tracer_provider = trace.TracerProvider(
sampling.StaticSampler(sampling.Decision.RECORD_AND_SAMPLE,)
sampling.StaticSampler(sampling.Decision.RECORD_AND_SAMPLE)
)

self.tracer = tracer_provider.get_tracer(__name__)
Expand Down