Repository URL to install this package:
|
Version:
0.2.1 ▾
|
import threading
from opentracing import Span as OpenTracingSpan
from opentracing.ext import tags as OTTags
from ddtrace.span import Span as DatadogSpan
from ddtrace.ext import errors
from .tags import Tags
from .span_context import SpanContext
class Span(OpenTracingSpan):
"""Datadog implementation of :class:`opentracing.Span`"""
def __init__(self, tracer, context, operation_name):
if context is not None:
context = SpanContext(ddcontext=context._dd_context,
baggage=context.baggage)
else:
context = SpanContext()
super(Span, self).__init__(tracer, context)
self.finished = False
self._lock = threading.Lock()
# use a datadog span
self._dd_span = DatadogSpan(tracer._dd_tracer, operation_name,
context=context._dd_context)
def finish(self, finish_time=None):
"""Finish the span.
This calls finish on the ddspan.
:param finish_time: specify a custom finish time with a unix timestamp
per time.time()
:type timestamp: float
"""
if self.finished:
return
# finish the datadog span
self._dd_span.finish(finish_time)
self.finished = True
def set_baggage_item(self, key, value):
"""Sets a baggage item in the span context of this span.
Baggage is used to propagate state between spans.
:param key: baggage item key
:type key: str
:param value: baggage item value
:type value: a type that can be compat.stringify()'d
:rtype: Span
:return: itself for chaining calls
"""
new_ctx = self.context.with_baggage_item(key, value)
with self._lock:
self._context = new_ctx
return self
def get_baggage_item(self, key):
"""Gets a baggage item from the span context of this span.
:param key: baggage item key
:type key: str
:rtype: str
:return: the baggage value for the given key or ``None``.
"""
return self.context.get_baggage_item(key)
def set_operation_name(self, operation_name):
"""Set the operation name."""
self._dd_span.name = operation_name
def log_kv(self, key_values, timestamp=None):
"""Add a log record to this span.
Passes on relevant opentracing key values onto the datadog span.
:param key_values: a dict of string keys and values of any type
:type key_values: dict
:param timestamp: a unix timestamp per time.time()
:type timestamp: float
:return: the span itself, for call chaining
:rtype: Span
"""
# match opentracing defined keys to datadog functionality
# opentracing/specification/blob/1be630515dafd4d2a468d083300900f89f28e24d/semantic_conventions.md#log-fields-table
for key, val in key_values.items():
if key == 'event' and val == 'error':
# TODO: not sure if it's actually necessary to set the error manually
self._dd_span.error = 1
self.set_tag('error', 1)
elif key == 'error' or key == 'error.object':
self.set_tag(errors.ERROR_TYPE, val)
elif key == 'message':
self.set_tag(errors.ERROR_MSG, val)
elif key == 'stack':
self.set_tag(errors.ERROR_STACK, val)
else:
pass
return self
def set_tag(self, key, value):
"""Set a tag on the span.
This sets the tag on the underlying datadog span.
"""
if key == Tags.SPAN_TYPE:
self._dd_span.span_type = value
elif key == Tags.SERVICE_NAME:
self._dd_span.service = value
elif key == Tags.RESOURCE_NAME or key == OTTags.DATABASE_STATEMENT:
self._dd_span.resource = value
elif key == OTTags.PEER_HOSTNAME:
self._dd_span.set_tag(Tags.TARGET_HOST, value)
elif key == OTTags.PEER_PORT:
self._dd_span.set_tag(Tags.TARGET_PORT, value)
elif key == Tags.SAMPLING_PRIORITY:
self._dd_span.context.sampling_priority = value
else:
self._dd_span.set_tag(key, value)
def _get_tag(self, key):
"""Gets a tag from the span.
This method retrieves the tag from the underlying datadog span.
"""
return self._dd_span.get_tag(key)
def _get_metric(self, key):
"""Gets a metric from the span.
This method retrieves the metric from the underlying datadog span.
"""
return self._dd_span.get_metric(key)
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type:
self._dd_span.set_exc_info(exc_type, exc_val, exc_tb)
# note: self.finish() AND _dd_span.__exit__ will call _span.finish() but
# it is idempotent
self._dd_span.__exit__(exc_type, exc_val, exc_tb)
self.finish()
def _associate_dd_span(self, ddspan):
"""Associates a DD span with this span."""
# get the datadog span context
self._dd_span = ddspan
self.context._dd_context = ddspan.context
@property
def _dd_context(self):
return self._dd_span.context