Skip to content

Commit

Permalink
Set status for ended spans
Browse files Browse the repository at this point in the history
Fixes #292
  • Loading branch information
ocelotl committed Nov 20, 2019
1 parent fb3e715 commit 118d6f8
Show file tree
Hide file tree
Showing 7 changed files with 273 additions and 174 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,13 @@
from pymongo import monitoring

from opentelemetry.trace import SpanKind
from opentelemetry.trace.status import Status, StatusCanonicalCode
from opentelemetry.trace.status import ( # pylint:disable=E0611
InternalStatus,
OkStatus,
Status,
StatusCanonicalCode,
UnknownStatus,
)

DATABASE_TYPE = "mongodb"
COMMAND_ATTRIBUTES = ["filter", "sort", "skip", "limit", "pipeline"]
Expand Down Expand Up @@ -72,7 +78,7 @@ def started(self, event: monitoring.CommandStartedEvent):
self._span_dict[_get_span_dict_key(event)] = span
except Exception as ex: # noqa pylint: disable=broad-except
if span is not None:
span.set_status(Status(StatusCanonicalCode.INTERNAL, str(ex)))
span.set_status(InternalStatus(str(ex)))
span.end()
self._remove_span(event)

Expand All @@ -82,7 +88,7 @@ def succeeded(self, event: monitoring.CommandSucceededEvent):
span.set_attribute(
"db.mongo.duration_micros", event.duration_micros
)
span.set_status(Status(StatusCanonicalCode.OK, event.reply))
span.set_status(OkStatus())
span.end()
self._remove_span(event)

Expand All @@ -92,7 +98,7 @@ def failed(self, event: monitoring.CommandFailedEvent):
span.set_attribute(
"db.mongo.duration_micros", event.duration_micros
)
span.set_status(Status(StatusCanonicalCode.UNKNOWN, event.failure))
span.set_status(UnknownStatus())
span.end()
self._remove_span(event)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ def test_succeeded(self):
self.assertIs(
span.status.canonical_code, trace_api.status.StatusCanonicalCode.OK
)
self.assertEqual(span.status.description, "reply")
self.assertIs(span.status.description, None)
self.assertIsNotNone(span.end_time)

def test_failed(self):
Expand All @@ -100,7 +100,7 @@ def test_failed(self):
span.status.canonical_code,
trace_api.status.StatusCanonicalCode.UNKNOWN,
)
self.assertEqual(span.status.description, "failure")
self.assertIs(span.status.description, None)
self.assertIsNotNone(span.end_time)

def test_multiple_commands(self):
Expand Down
33 changes: 30 additions & 3 deletions opentelemetry-api/src/opentelemetry/trace/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,11 @@
import typing
from contextlib import contextmanager

from opentelemetry.trace.status import Status
from opentelemetry.trace.status import ( # type: ignore # pylint:disable=E0611
Status,
StatusError,
UnknownStatus,
)
from opentelemetry.util import loader, types

# TODO: quarantine
Expand Down Expand Up @@ -124,10 +128,12 @@ class SpanKind(enum.Enum):
https://github.com/open-telemetry/opentelemetry-specification/pull/226.
"""

#: Default value. Indicates that the span is used internally in the application.
#: 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.
#: 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.
Expand All @@ -147,6 +153,10 @@ class SpanKind(enum.Enum):
class Span:
"""A span represents a single operation within a trace."""

def __init__(self, auto_update_status: bool = False):
# FIXME what is the standard to follow for documentation of attributes?
self._auto_update_status = auto_update_status

def start(self, start_time: typing.Optional[int] = None) -> None:
"""Sets the current time as the span's start time.
Expand Down Expand Up @@ -223,6 +233,9 @@ def set_status(self, status: Status) -> None:
Span status, which is OK.
"""

def get_status(self) -> Status:
"""Gets the Status of the Span."""

def __enter__(self) -> "Span":
"""Invoked when `Span` is used as a context manager.
Expand All @@ -237,8 +250,20 @@ def __exit__(
exc_tb: typing.Optional[python_types.TracebackType],
) -> None:
"""Ends context manager and calls `end` on the `Span`."""

self.end()

if (
exc_val is not None
and self.get_status is None
and self._auto_update_status
):
if isinstance(exc_val, StatusError):
self.set_status(exc_val.status)

else:
self.set_status(UnknownStatus)


class TraceOptions(int):
"""A bitmask that represents options specific to the trace.
Expand Down Expand Up @@ -353,6 +378,7 @@ class DefaultSpan(Span):
"""

def __init__(self, context: "SpanContext") -> None:
super(DefaultSpan, self).__init__()
self._context = context

def get_context(self) -> "SpanContext":
Expand Down Expand Up @@ -501,6 +527,7 @@ def create_span(
kind: SpanKind = SpanKind.INTERNAL,
attributes: typing.Optional[types.Attributes] = None,
links: typing.Sequence[Link] = (),
**kwargs: bool,
) -> "Span":
"""Creates a span.
Expand Down
Loading

0 comments on commit 118d6f8

Please sign in to comment.