Skip to content
Draft
Show file tree
Hide file tree
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
90 changes: 21 additions & 69 deletions ddtrace/_trace/_span_link.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,10 @@
s1.link_span(s2.context, link_attributes)
"""

import dataclasses
from enum import Enum
from typing import Optional

from ddtrace.internal.utils.formats import flatten_key_value
from ddtrace.internal.native._native import SpanLinkData


class SpanLinkKind(Enum):
Expand All @@ -39,13 +38,7 @@ class SpanLinkKind(Enum):
SPAN_POINTER = "span-pointer" # Should not be used on normal SpanLinks.


def _id_not_zero(self, attribute_name, value):
if not value > 0:
raise ValueError(f"{attribute_name} must be > 0. Value is {value}")


@dataclasses.dataclass
class SpanLink:
class SpanLink(SpanLinkData):
"""
TraceId [required]: The span's 128-bit Trace ID
SpanId [required]: The span's 64-bit Span ID
Expand All @@ -59,66 +52,25 @@ class SpanLink:
value is either a string, bool, number or an array of primitive type values.
"""

trace_id: int
span_id: int
tracestate: Optional[str] = None
flags: Optional[int] = None
attributes: dict = dataclasses.field(default_factory=dict)
_dropped_attributes: int = 0

def __post_init__(self):
_id_not_zero(self, "trace_id", self.trace_id)
_id_not_zero(self, "span_id", self.span_id)

@property
def name(self):
return self.attributes["link.name"]

@name.setter
def name(self, value):
self.attributes["link.name"] = value

@property
def kind(self) -> Optional[str]:
return self.attributes.get("link.kind")

@kind.setter
def kind(self, value: str) -> None:
self.attributes["link.kind"] = value

def _drop_attribute(self, key):
if key not in self.attributes:
raise KeyError(f"Invalid key: {key}")
del self.attributes[key]
self._dropped_attributes += 1

def to_dict(self):
d = {
"trace_id": "{:032x}".format(self.trace_id),
"span_id": "{:016x}".format(self.span_id),
}
if self.attributes:
d["attributes"] = {}
for k, v in self.attributes.items():
# flatten all values with the type list, tuple and set
for k1, v1 in flatten_key_value(k, v).items():
# convert all values to string
if isinstance(v1, str):
d["attributes"][k1] = v1
elif isinstance(v1, bool):
# convert bool to lowercase string to be consistent with json encoding
d["attributes"][k1] = str(v1).lower()
else:
d["attributes"][k1] = str(v1)

if self._dropped_attributes > 0:
d["dropped_attributes_count"] = self._dropped_attributes
if self.tracestate:
d["tracestate"] = self.tracestate
if self.flags is not None:
d["flags"] = self.flags

return d
__slots__ = []

def __init__(
self,
trace_id: int,
span_id: int,
tracestate: Optional[str] = None,
flags: Optional[int] = None,
attributes: Optional[dict] = None,
_dropped_attributes: int = 0,
):
super().__init__(
trace_id=trace_id,
span_id=span_id,
tracestate=tracestate,
flags=flags,
attributes=attributes or {},
_dropped_attributes=_dropped_attributes,
)

def __repr__(self) -> str:
return (
Expand Down
10 changes: 5 additions & 5 deletions ddtrace/_trace/processor/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ def on_span_start(self, _: Span) -> None:
def on_span_finish(self, span: Span) -> None:
# DEV: Update span after finished to avoid race condition
if span._is_top_level:
span._metrics["_dd.top_level"] = 1 # PERF: avoid setting via Span.set_metric
span._set_metrics_inner("_dd.top_level", 1) # PERF: avoid setting via Span.set_metric


class ServiceNameProcessor(TraceProcessor):
Expand Down Expand Up @@ -255,9 +255,9 @@ def process_trace(self, trace: List[Span]) -> Optional[List[Span]]:
trace_id_hob = _get_64_highest_order_bits_as_hex(span.trace_id)
span.set_tag_str(HIGHER_ORDER_TRACE_ID_BITS, trace_id_hob)

if LAST_DD_PARENT_ID_KEY in span._meta and span._parent is not None:
if span._get_meta_inner(LAST_DD_PARENT_ID_KEY) is not None and span._parent is not None:
# we should only set the last parent id on local root spans
del span._meta[LAST_DD_PARENT_ID_KEY]
span._delete_meta_inner(LAST_DD_PARENT_ID_KEY)

return trace

Expand Down Expand Up @@ -334,7 +334,7 @@ def on_span_start(self, span: Span) -> None:
with self._lock:
trace = self._traces[span.trace_id]
trace.spans.append(span)
integration_name = span._meta.get(COMPONENT, span._span_api)
integration_name = span._get_meta_inner(COMPONENT) or span._span_api

self._span_metrics["spans_created"][integration_name] += 1
self._queue_span_count_metrics("spans_created", "integration_name")
Expand All @@ -343,7 +343,7 @@ def on_span_start(self, span: Span) -> None:
def on_span_finish(self, span: Span) -> None:
# Acquire lock to get finished and update trace.spans
with self._lock:
integration_name = span._meta.get(COMPONENT, span._span_api)
integration_name = span._get_meta_inner(COMPONENT) or span._span_api
self._span_metrics["spans_finished"][integration_name] += 1

if span.trace_id not in self._traces:
Expand Down
6 changes: 2 additions & 4 deletions ddtrace/_trace/sampling_rule.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,11 @@ def tags_match(self, span: Span) -> bool:
if not self.tags:
return True

meta = span._meta or {}
metrics = span._metrics or {}
if not meta and not metrics:
if span._is_meta_empty_inner() and span._is_meta_empty_inner():
return False

for tag_key, pattern in self.tags.items():
value = meta.get(tag_key, metrics.get(tag_key))
value = span._get_metrics_inner(tag_key) or span._get_metrics_inner(tag_key)
if value is None:
# If the tag is not present, we failed the match
# (Metrics and meta do not support the value None)
Expand Down
Loading