Skip to content

Add default invalid SpanContext #47

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

Merged
merged 7 commits into from
Jul 15, 2019
Merged
Changes from all 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
93 changes: 73 additions & 20 deletions opentelemetry-api/src/opentelemetry/trace/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
import typing
from opentelemetry import loader


class Span:
"""A span represents a single operation within a trace."""

Expand Down Expand Up @@ -99,6 +100,47 @@ def get_context(self) -> 'SpanContext':
"""


class TraceOptions(int):
"""A bitmask that represents options specific to the trace.

The only supported option is the "recorded" flag (``0x01``). If set, this
flag indicates that the trace may have been recorded upstream.

See the `W3C Trace Context - Traceparent`_ spec for details.

.. _W3C Trace Context - Traceparent:
https://www.w3.org/TR/trace-context/#trace-flags
"""
DEFAULT = 0x00
RECORDED = 0x01

@classmethod
def get_default(cls) -> 'TraceOptions':
return cls(cls.DEFAULT)


DEFAULT_TRACEOPTIONS = TraceOptions.get_default()
Copy link
Contributor

Choose a reason for hiding this comment

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

One thing we need to decide is whether we want these as module or class level members.

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't have a strong preference, but we'd have to do the assignment (e.g. TraceOptions.DEFAULT = TraceOptions.get_default) outside the class definition anyway.



class TraceState(typing.Dict[str, str]):
"""A list of key-value pairs representing vendor-specific trace info.

Keys and values are strings of up to 256 printable US-ASCII characters.
Implementations should conform to the the `W3C Trace Context - Tracestate`_
spec, which describes additional restrictions on valid field values.

.. _W3C Trace Context - Tracestate:
https://www.w3.org/TR/trace-context/#tracestate-field
"""

@classmethod
def get_default(cls) -> 'TraceState':
return cls()


DEFAULT_TRACESTATE = TraceState.get_default()


class SpanContext:
"""The state of a Span to propagate between processes.

Expand All @@ -113,11 +155,31 @@ class SpanContext:
"""

def __init__(self,
trace_id: str,
span_id: str,
trace_id: int,
span_id: int,
options: 'TraceOptions',
state: 'TraceState') -> None:
pass
self.trace_id = trace_id
self.span_id = span_id
self.options = options
self.state = state

def is_valid(self) -> bool:
"""Get whether this `SpanContext` is valid.

A `SpanContext` is said to be invalid if its trace ID or span ID is
invalid (i.e. ``0``).

Returns:
True if the `SpanContext` is valid, false otherwise.
"""


INVALID_SPAN_ID = 0
INVALID_TRACE_ID = 0
INVALID_SPAN_CONTEXT = SpanContext(INVALID_TRACE_ID, INVALID_SPAN_ID,
DEFAULT_TRACEOPTIONS, DEFAULT_TRACESTATE)


class Tracer:
"""Handles span creation and in-process context propagation.
Expand Down Expand Up @@ -239,39 +301,31 @@ def use_span(self, span: 'Span') -> typing.Iterator[None]:
"""


# TODO
class TraceOptions(int):
pass


# TODO
class TraceState(typing.Dict[str, str]):
pass


_TRACER: typing.Optional[Tracer] = None
_TRACER_FACTORY: typing.Optional[
typing.Callable[[typing.Type[Tracer]], typing.Optional[Tracer]]] = None


def tracer() -> Tracer:
"""Gets the current global :class:`~.Tracer` object.
If there isn't one set yet, a default will be loaded."""

global _TRACER, _TRACER_FACTORY #pylint:disable=global-statement
If there isn't one set yet, a default will be loaded.
"""
global _TRACER, _TRACER_FACTORY # pylint:disable=global-statement

if _TRACER is None:
#pylint:disable=protected-access
# pylint:disable=protected-access
_TRACER = loader._load_impl(Tracer, _TRACER_FACTORY)
del _TRACER_FACTORY

return _TRACER


def set_preferred_tracer_implementation(
factory: typing.Callable[
[typing.Type[Tracer]], typing.Optional[Tracer]]
) -> None:
"""Sets a factory function which may be used to create the tracer
implementation.
"""Set the factory to be used to create the tracer.

See :mod:`opentelemetry.loader` for details.

Expand All @@ -280,8 +334,7 @@ def set_preferred_tracer_implementation(
Args:
factory: Callback that should create a new :class:`Tracer` instance.
"""

global _TRACER_FACTORY #pylint:disable=global-statement
global _TRACER_FACTORY # pylint:disable=global-statement

if _TRACER:
raise RuntimeError("Tracer already loaded.")
Expand Down