mirror of
https://github.com/open-telemetry/opentelemetry-python-contrib.git
synced 2025-07-30 21:56:07 +08:00
168 lines
5.3 KiB
Python
168 lines
5.3 KiB
Python
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
|