Skip to content

Commit

Permalink
Refactor current span handling for newly created spans. (#198)
Browse files Browse the repository at this point in the history
1. Make Tracer.start_span() simply create and start the Span,
   without setting it as the current instance.
2. Add an extra Tracer.start_as_current_span() to create the
   Span and set it as the current instance automatically.

Co-Authored-By: Chris Kleinknecht <libc@google.com>
  • Loading branch information
2 people authored and Oberon00 committed Oct 15, 2019
1 parent 3c513e3 commit 63f559e
Show file tree
Hide file tree
Showing 3 changed files with 171 additions and 54 deletions.
89 changes: 72 additions & 17 deletions opentelemetry-api/src/opentelemetry/trace/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,16 @@
to use the API package alone without a supporting implementation.
The tracer supports creating spans that are "attached" or "detached" from the
context. By default, new spans are "attached" to the context in that they are
context. New spans are "attached" to the context in that they are
created as children of the currently active span, and the newly-created span
becomes the new active span::
can optionally become the new active span::
from opentelemetry.trace import tracer
# Create a new root span, set it as the current span in context
with tracer.start_span("parent"):
with tracer.start_as_current_span("parent"):
# Attach a new child and update the current span
with tracer.start_span("child"):
with tracer.start_as_current_span("child"):
do_work():
# Close child span, set parent as current
# Close parent span, set default span as current
Expand All @@ -62,6 +62,7 @@
"""

import enum
import types as python_types
import typing
from contextlib import contextmanager

Expand Down Expand Up @@ -226,6 +227,26 @@ def is_recording_events(self) -> bool:
events with the add_event operation and attributes using set_attribute.
"""

def __enter__(self) -> "Span":
"""Invoked when `Span` is used as a context manager.
Returns the `Span` itself.
"""
return self

def __exit__(
self,
exc_type: typing.Optional[typing.Type[BaseException]],
exc_val: typing.Optional[BaseException],
exc_tb: typing.Optional[python_types.TracebackType],
) -> typing.Optional[bool]:
"""Ends context manager and calls `end` on the `Span`.
Returns False.
"""
self.end()
return False


class TraceOptions(int):
"""A bitmask that represents options specific to the trace.
Expand Down Expand Up @@ -376,46 +397,79 @@ def get_current_span(self) -> "Span":
# pylint: disable=no-self-use
return INVALID_SPAN

@contextmanager # type: ignore
def start_span(
self,
name: str,
parent: ParentSpan = CURRENT_SPAN,
kind: SpanKind = SpanKind.INTERNAL,
) -> typing.Iterator["Span"]:
"""Context manager for span creation.
) -> "Span":
"""Starts a span.
Create a new span. Start the span and set it as the current span in
this tracer's context.
Create a new span. Start the span without setting it as the current
span in this tracer's context.
By default the current span will be used as parent, but an explicit
parent can also be specified, either a `Span` or a `SpanContext`. If
the specified value is `None`, the created span will be a root span.
On exiting the context manager stop the span and set its parent as the
The span can be used as context manager. On exiting, the span will be
ended.
Example::
# tracer.get_current_span() will be used as the implicit parent.
# If none is found, the created span will be a root instance.
with tracer.start_span("one") as child:
child.add_event("child's event")
Applications that need to set the newly created span as the current
instance should use :meth:`start_as_current_span` instead.
Args:
name: The name of the span to be created.
parent: The span's parent. Defaults to the current span.
kind: The span's kind (relationship to parent). Note that is
meaningful even if there is no parent.
Returns:
The newly-created span.
"""
# pylint: disable=unused-argument,no-self-use
return INVALID_SPAN

@contextmanager # type: ignore
def start_as_current_span(
self,
name: str,
parent: ParentSpan = CURRENT_SPAN,
kind: SpanKind = SpanKind.INTERNAL,
) -> typing.Iterator["Span"]:
"""Context manager for creating a new span and set it
as the current span in this tracer's context.
On exiting the context manager stops the span and set its parent as the
current span.
Example::
with tracer.start_span("one") as parent:
with tracer.start_as_current_span("one") as parent:
parent.add_event("parent's event")
with tracer.start_span("two") as child:
with tracer.start_as_current_span("two") as child:
child.add_event("child's event")
tracer.get_current_span() # returns child
tracer.get_current_span() # returns parent
tracer.get_current_span() # returns previously active span
This is a convenience method for creating spans attached to the
tracer's context. Applications that need more control over the span
lifetime should use :meth:`create_span` instead. For example::
lifetime should use :meth:`start_span` instead. For example::
with tracer.start_span(name) as span:
with tracer.start_as_current_span(name) as span:
do_work()
is equivalent to::
span = tracer.create_span(name)
span.start()
span = tracer.start_span(name)
with tracer.use_span(span, end_on_exit=True):
do_work()
Expand All @@ -428,6 +482,7 @@ def start_span(
Yields:
The newly-created span.
"""

# pylint: disable=unused-argument,no-self-use
yield INVALID_SPAN

Expand All @@ -451,7 +506,7 @@ def create_span(
Applications that need to create spans detached from the tracer's
context should use this method.
with tracer.start_span(name) as span:
with tracer.start_as_current_span(name) as span:
do_work()
This is equivalent to::
Expand Down
17 changes: 13 additions & 4 deletions opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -322,19 +322,28 @@ def get_current_span(self):
"""See `opentelemetry.trace.Tracer.get_current_span`."""
return self._current_span_slot.get()

@contextmanager
def start_span(
self,
name: str,
parent: trace_api.ParentSpan = trace_api.Tracer.CURRENT_SPAN,
kind: trace_api.SpanKind = trace_api.SpanKind.INTERNAL,
) -> typing.Iterator[trace_api.Span]:
) -> "Span":
"""See `opentelemetry.trace.Tracer.start_span`."""

span = self.create_span(name, parent, kind)
span.start()
with self.use_span(span, end_on_exit=True):
yield span
return span

def start_as_current_span(
self,
name: str,
parent: trace_api.ParentSpan = trace_api.Tracer.CURRENT_SPAN,
kind: trace_api.SpanKind = trace_api.SpanKind.INTERNAL,
) -> typing.Iterator[trace_api.Span]:
"""See `opentelemetry.trace.Tracer.start_as_current_span`."""

span = self.start_span(name, parent, kind)
return self.use_span(span, end_on_exit=True)

def create_span(
self,
Expand Down
Loading

0 comments on commit 63f559e

Please sign in to comment.