Skip to content

Commit

Permalink
Add SpanKind option at span creation (closes open-telemetry#103). (op…
Browse files Browse the repository at this point in the history
  • Loading branch information
Oberon00 authored and reyang committed Sep 18, 2019
1 parent b6e97fc commit dbb3be8
Show file tree
Hide file tree
Showing 7 changed files with 62 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@

from requests.sessions import Session

from opentelemetry.trace import SpanKind


# NOTE: Currently we force passing a tracer. But in turn, this forces the user
# to configure a SDK before enabling this integration. In turn, this means that
Expand Down Expand Up @@ -61,11 +63,8 @@ def instrumented_request(self, method, url, *args, **kwargs):
path = "<URL parses to None>"
path = parsed_url.path

with tracer.start_span(path) as span:
with tracer.start_span(path, kind=SpanKind.CLIENT) as span:
span.set_attribute("component", "http")
# TODO: The OTel spec says "SpanKind" MUST be "Client" but that
# seems to be a leftover, as Spans have no explicit field for
# kind.
span.set_attribute("http.method", method.upper())
span.set_attribute("http.url", url)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ def test_basic(self):
url = "https://www.example.org/foo/bar?x=y#top"
requests.get(url=url)
self.assertEqual(1, len(self.send.call_args_list))
self.tracer.start_span.assert_called_with("/foo/bar")
self.tracer.start_span.assert_called_with(
"/foo/bar", kind=trace.SpanKind.CLIENT
)
self.span_context_manager.__enter__.assert_called_with()
self.span_context_manager.__exit__.assert_called_with(None, None, None)
self.assertEqual(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ def __call__(self, environ, start_response):
tracer = trace.tracer()
path_info = environ["PATH_INFO"] or "/"

with tracer.start_span(path_info) as span:
with tracer.start_span(path_info, kind=trace.SpanKind.SERVER) as span:
self._add_request_attributes(span, environ)
start_response = self._create_start_response(span, start_response)

Expand Down
4 changes: 3 additions & 1 deletion ext/opentelemetry-ext-wsgi/tests/test_wsgi_middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,9 @@ def validate_response(self, response, error=None):
self.assertIsNone(self.exc_info)

# Verify that start_span has been called
self.start_span.assert_called_once_with("/")
self.start_span.assert_called_once_with(
"/", kind=trace_api.SpanKind.SERVER
)

def test_basic_wsgi_call(self):
app = OpenTelemetryMiddleware(simple_wsgi)
Expand Down
42 changes: 40 additions & 2 deletions opentelemetry-api/src/opentelemetry/trace/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
.. versionadded:: 0.1.0
"""

import enum
import typing
from contextlib import contextmanager

Expand Down Expand Up @@ -111,6 +112,33 @@ def timestamp(self) -> int:
return self._timestamp


class SpanKind(enum.Enum):
"""Specifies additional details on how this span relates to its parent span.
Note that this enumeration is experimental and likely to change. See
https://github.com/open-telemetry/opentelemetry-specification/pull/226.
"""

#: Default value. Indicates that the span is used internally in the application.
INTERNAL = 0

#: Indicates that the span describes an operation that handles a remote request.
SERVER = 1

#: Indicates that the span describes a request to some remote service.
CLIENT = 2

#: Indicates that the span describes a producer sending a message to a
#: broker. Unlike client and server, there is usually no direct critical
#: path latency relationship between producer and consumer spans.
PRODUCER = 3

#: Indicates that the span describes consumer receiving a message from a
#: broker. Unlike client and server, there is usually no direct critical
#: path latency relationship between producer and consumer spans.
CONSUMER = 4


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

Expand Down Expand Up @@ -350,7 +378,10 @@ def get_current_span(self) -> "Span":

@contextmanager # type: ignore
def start_span(
self, name: str, parent: ParentSpan = CURRENT_SPAN
self,
name: str,
parent: ParentSpan = CURRENT_SPAN,
kind: SpanKind = SpanKind.INTERNAL,
) -> typing.Iterator["Span"]:
"""Context manager for span creation.
Expand Down Expand Up @@ -390,6 +421,8 @@ def start_span(
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.
Yields:
The newly-created span.
Expand All @@ -398,7 +431,10 @@ def start_span(
yield INVALID_SPAN

def create_span(
self, name: str, parent: ParentSpan = CURRENT_SPAN
self,
name: str,
parent: ParentSpan = CURRENT_SPAN,
kind: SpanKind = SpanKind.INTERNAL,
) -> "Span":
"""Creates a span.
Expand Down Expand Up @@ -426,6 +462,8 @@ def create_span(
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.
Expand Down
8 changes: 7 additions & 1 deletion opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ def __init__(
attributes: types.Attributes = None, # TODO
events: typing.Sequence[trace_api.Event] = None, # TODO
links: typing.Sequence[trace_api.Link] = None, # TODO
kind: trace_api.SpanKind = trace_api.SpanKind.INTERNAL,
span_processor: SpanProcessor = SpanProcessor(),
) -> None:

Expand All @@ -253,6 +254,8 @@ def __init__(
self.attributes = attributes
self.events = events
self.links = links
self.kind = kind

self.span_processor = span_processor
self._lock = threading.Lock()

Expand Down Expand Up @@ -417,15 +420,17 @@ 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["Span"]:
"""See `opentelemetry.trace.Tracer.start_span`."""
with self.use_span(self.create_span(name, parent)) as span:
with self.use_span(self.create_span(name, parent, kind)) as span:
yield span

def create_span(
self,
name: str,
parent: trace_api.ParentSpan = trace_api.Tracer.CURRENT_SPAN,
kind: trace_api.SpanKind = trace_api.SpanKind.INTERNAL,
) -> "Span":
"""See `opentelemetry.trace.Tracer.create_span`."""
span_id = generate_span_id()
Expand All @@ -451,6 +456,7 @@ def create_span(
context=context,
parent=parent,
span_processor=self._active_span_processor,
kind=kind,
)

@contextmanager
Expand Down
6 changes: 5 additions & 1 deletion opentelemetry-sdk/tests/trace/test_trace.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,14 @@ def test_start_span_implicit(self):

self.assertIsNotNone(root.start_time)
self.assertIsNone(root.end_time)
self.assertEqual(root.kind, trace_api.SpanKind.INTERNAL)

with tracer.start_span("child") as child:
with tracer.start_span(
"child", kind=trace_api.SpanKind.CLIENT
) as child:
self.assertIs(tracer.get_current_span(), child)
self.assertIs(child.parent, root)
self.assertEqual(child.kind, trace_api.SpanKind.CLIENT)

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

0 comments on commit dbb3be8

Please sign in to comment.