Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Report dropped attributes/events/links for otlp/jaeger/zipkin exporters #1893

Merged
merged 25 commits into from
Jun 30, 2021
Merged
Show file tree
Hide file tree
Changes from 6 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
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,9 @@ def _translate_span(self, span: ReadableSpan) -> model_pb2.Span:
tags = self._extract_tags(span)
refs = self._extract_refs(span)
logs = self._extract_logs(span)
dropped_tags = self._dropped_tags(span)
if dropped_tags:
codeboten marked this conversation as resolved.
Show resolved Hide resolved
tags.extend(dropped_tags)

flags = int(ctx.trace_flags)

Expand Down Expand Up @@ -368,3 +371,16 @@ def _extract_logs(
logs.append(model_pb2.Log(timestamp=event_ts, fields=fields))

return logs

def _dropped_tags(
self, span: ReadableSpan
) -> Sequence[model_pb2.KeyValue]:
tags = []
if span.dropped_attributes:
tags.append(_get_long_key_value("otel.dropped_attributes_count", span.dropped_attributes))
if span.dropped_events:
tags.append(_get_long_key_value("otel.dropped_events_count", span.dropped_events))
if span.dropped_links:
tags.append(_get_long_key_value("otel.dropped_links_count", span.dropped_links))

return tags
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import SpanExportResult
from opentelemetry.sdk.util.instrumentation import InstrumentationInfo
from opentelemetry.test.spantestutil import get_span_with_dropped_attributes_events_links
from opentelemetry.trace.status import Status, StatusCode


Expand Down Expand Up @@ -471,9 +472,24 @@ def test_export_span_service_name(self):
exporter._grpc_client = client_mock
exporter.export([span])
self.assertEqual(exporter.service_name, "test")

def test_dropped_values(self):
span = get_span_with_dropped_attributes_events_links()
translate = Translate([span])

# pylint: disable=protected-access
spans = translate._translate(pb_translator.ProtobufTranslator("svc"))
tags_by_keys = {
tag.key: tag.v_str or tag.v_int64
for tag in spans[0].tags
}
self.assertEqual(
1, tags_by_keys["otel.dropped_links_count"]
)
self.assertEqual(
2, tags_by_keys["otel.dropped_attributes_count"]
)
self.assertEqual(
3, tags_by_keys["otel.dropped_events_count"]
)

class MockResponse:
def __init__(self, status_code):
self.status_code = status_code
self.text = status_code
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,9 @@ def _translate_span(self, span: ReadableSpan) -> TCollector.Span:
tags = self._extract_tags(span)
refs = self._extract_refs(span)
logs = self._extract_logs(span)
dropped_tags = self._dropped_tags(span)
if dropped_tags:
tags.extend(dropped_tags)

flags = int(ctx.trace_flags)

Expand Down Expand Up @@ -285,3 +288,22 @@ def _extract_logs(
)

return logs

def _dropped_tags(self, span: ReadableSpan) -> Sequence[TCollector.Tag]:
ocelotl marked this conversation as resolved.
Show resolved Hide resolved
tags = []
if span.dropped_attributes:
tags.append(
_get_long_tag(
"otel.dropped_attributes_count", span.dropped_attributes
)
)
if span.dropped_events:
tags.append(
_get_long_tag("otel.dropped_events_count", span.dropped_events)
)
if span.dropped_links:
tags.append(
_get_long_tag("otel.dropped_links_count", span.dropped_links)
)

return tags
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
from opentelemetry.sdk.util.instrumentation import InstrumentationInfo
from opentelemetry.trace import SpanKind
from opentelemetry.trace.status import Status, StatusCode
from opentelemetry.test.spantestutil import get_span_with_dropped_attributes_events_links


class TestJaegerExporter(unittest.TestCase):
Expand Down Expand Up @@ -559,7 +560,9 @@ def test_max_tag_value_length(self):
# pylint: disable=protected-access
spans = translate._translate(ThriftTranslator())
tags_by_keys = {
tag.key: tag.vStr for tag in spans[0].tags if tag.vType == 0
tag.key: tag.vStr
for tag in spans[0].tags
if tag.vType == jaeger.TagType.STRING
}
self.assertEqual(
"hello_world hello_world hello_world", tags_by_keys["key_string"]
Expand All @@ -576,12 +579,30 @@ def test_max_tag_value_length(self):
# pylint: disable=protected-access
spans = translate._translate(ThriftTranslator(max_tag_value_length=5))
tags_by_keys = {
tag.key: tag.vStr for tag in spans[0].tags if tag.vType == 0
tag.key: tag.vStr
for tag in spans[0].tags
if tag.vType == jaeger.TagType.STRING
}
self.assertEqual("hello", tags_by_keys["key_string"])
self.assertEqual("('tup", tags_by_keys["key_tuple"])
self.assertEqual("some_", tags_by_keys["key_resource"])

def test_dropped_values(self):
span = get_span_with_dropped_attributes_events_links()
translate = Translate([span])

# pylint: disable=protected-access
spans = translate._translate(ThriftTranslator(max_tag_value_length=5))
tags_by_keys = {
tag.key: tag.vLong
for tag in spans[0].tags
if tag.vType == jaeger.TagType.LONG
}

self.assertEqual(1, tags_by_keys["otel.dropped_links_count"])
self.assertEqual(2, tags_by_keys["otel.dropped_attributes_count"])
self.assertEqual(3, tags_by_keys["otel.dropped_events_count"])

def test_agent_client_split(self):
agent_client = jaeger_exporter.AgentClientUDP(
host_name="localhost",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,20 @@ def _extract_tags_from_span(self, span: Span) -> Dict[str, str]:
tags.update({"otel.status_code": span.status.status_code.name})
if span.status.status_code is StatusCode.ERROR:
tags.update({"error": span.status.description or ""})

if span.dropped_attributes:
tags.update(
{"otel.dropped_attributes_count": str(span.dropped_attributes)}
)

if span.dropped_events:
tags.update(
{"otel.dropped_events_count": str(span.dropped_events)}
)

if span.dropped_links:
tags.update({"otel.dropped_links_count": str(span.dropped_links)})

return tags

def _extract_annotations_from_events(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
from opentelemetry.exporter.zipkin.json.v1 import JsonV1Encoder
from opentelemetry.exporter.zipkin.node_endpoint import NodeEndpoint
from opentelemetry.sdk import trace
from opentelemetry.test.spantestutil import (
get_span_with_dropped_attributes_events_links,
)
from opentelemetry.trace import TraceFlags, format_span_id, format_trace_id

from .common_tests import TEST_SERVICE_NAME, CommonEncoderTestCases
Expand Down Expand Up @@ -249,3 +252,22 @@ def _test_encode_max_tag_length(self, max_tag_value_length: int):
[otel_span], NodeEndpoint()
),
)

def test_dropped(self):
otel_span = get_span_with_dropped_attributes_events_links()
annotations = JsonV1Encoder()._encode_span(otel_span, "test")[
"binaryAnnotations"
]
asserts = 0
for annotation in annotations:
if annotation.get("key") == "otel.dropped_links_count":
self.assertEqual("1", annotation.get("value"))
asserts += 1
if annotation.get("key") == "otel.dropped_attributes_count":
self.assertEqual("2", annotation.get("value"))
asserts += 1
if annotation.get("key") == "otel.dropped_events_count":
self.assertEqual("3", annotation.get("value"))
asserts += 1

self.assertEqual(3, asserts)
ocelotl marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
from opentelemetry.exporter.zipkin.json.v2 import JsonV2Encoder
from opentelemetry.exporter.zipkin.node_endpoint import NodeEndpoint
from opentelemetry.sdk import trace
from opentelemetry.test.spantestutil import (
get_span_with_dropped_attributes_events_links,
)
from opentelemetry.trace import SpanKind, TraceFlags

from .common_tests import TEST_SERVICE_NAME, CommonEncoderTestCases
Expand Down Expand Up @@ -203,3 +206,11 @@ def _test_encode_max_tag_length(self, max_tag_value_length: int):
[otel_span], NodeEndpoint()
),
)

def test_dropped(self):
otel_span = get_span_with_dropped_attributes_events_links()
tags = JsonV2Encoder()._encode_span(otel_span, "test")["tags"]

self.assertEqual("1", tags.get("otel.dropped_links_count"))
ocelotl marked this conversation as resolved.
Show resolved Hide resolved
self.assertEqual("2", tags.get("otel.dropped_attributes_count"))
self.assertEqual("3", tags.get("otel.dropped_events_count"))
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from opentelemetry.exporter.zipkin.node_endpoint import NodeEndpoint
from opentelemetry.exporter.zipkin.proto.http.v2 import ProtobufEncoder
from opentelemetry.exporter.zipkin.proto.http.v2.gen import zipkin_pb2
from opentelemetry.test.spantestutil import get_span_with_dropped_attributes_events_links
from opentelemetry.trace import SpanKind

from .common_tests import TEST_SERVICE_NAME, CommonEncoderTestCases
Expand Down Expand Up @@ -232,3 +233,12 @@ def _test_encode_max_tag_length(self, max_tag_value_length: int):
)

self.assertEqual(actual_output, expected_output)

def test_dropped(self):
otel_span = get_span_with_dropped_attributes_events_links()
# pylint: disable=no-member
tags = ProtobufEncoder()._encode_span(otel_span, zipkin_pb2.Endpoint()).tags

self.assertEqual('1', tags.get("otel.dropped_links_count"))
self.assertEqual('2', tags.get("otel.dropped_attributes_count"))
self.assertEqual('3', tags.get("otel.dropped_events_count"))
6 changes: 6 additions & 0 deletions opentelemetry-sdk/src/opentelemetry/sdk/resources/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,12 @@ def attributes(self) -> Attributes:
def schema_url(self) -> str:
return self._schema_url

@property
def dropped_attributes(self) -> int:
if self._attributes:
return self._attributes.dropped
return 0

def merge(self, other: "Resource") -> "Resource":
"""Merges this resource and an updating resource into a new `Resource`.

Expand Down
24 changes: 24 additions & 0 deletions opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,12 @@ def __init__(
@property
def attributes(self) -> types.Attributes:
return self._attributes

@property
def dropped_attributes(self) -> int:
lzchen marked this conversation as resolved.
Show resolved Hide resolved
if self._attributes:
return self._attributes.dropped
return 0


def _check_span_ended(func):
Expand Down Expand Up @@ -365,6 +371,24 @@ def __init__(
self._resource = resource
self._status = status

@property
def dropped_attributes(self) -> int:
if self._attributes:
return self._attributes.dropped
lzchen marked this conversation as resolved.
Show resolved Hide resolved
return 0

@property
def dropped_events(self) -> int:
if self._events:
codeboten marked this conversation as resolved.
Show resolved Hide resolved
return self._events.dropped
return 0

@property
def dropped_links(self) -> int:
if self._links:
return self._links.dropped
return 0

@property
def name(self) -> str:
return self._name
Expand Down
7 changes: 7 additions & 0 deletions opentelemetry-sdk/tests/trace/test_trace.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
from opentelemetry.sdk.trace.id_generator import RandomIdGenerator
from opentelemetry.sdk.util import ns_to_iso_str
from opentelemetry.sdk.util.instrumentation import InstrumentationInfo
from opentelemetry.test.spantestutil import get_span_with_dropped_attributes_events_links
from opentelemetry.trace import StatusCode
from opentelemetry.util._time import _time_ns

Expand Down Expand Up @@ -1444,3 +1445,9 @@ def test_span_no_limits_code(self):
)
)
)

def test_dropped_attributes(self):
span = get_span_with_dropped_attributes_events_links()
self.assertEqual(1, span.dropped_links)
self.assertEqual(2, span.dropped_attributes)
self.assertEqual(3, span.dropped_events)
38 changes: 37 additions & 1 deletion tests/util/src/opentelemetry/test/spantestutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
from importlib import reload

from opentelemetry import trace as trace_api
from opentelemetry.sdk.trace import TracerProvider, export
from opentelemetry.sdk import trace as trace_sdk
from opentelemetry.sdk.trace import Resource, TracerProvider, export
from opentelemetry.sdk.trace.export.in_memory_span_exporter import (
InMemorySpanExporter,
)
Expand All @@ -41,3 +42,38 @@ def tearDownClass(cls):
def setUp(self):
self.memory_exporter = _MEMORY_EXPORTER
self.memory_exporter.clear()


def get_span_with_dropped_attributes_events_links():
attributes = {}
for i in range(130):
attributes["key{}".format(i)] = ["value{}".format(i)]
links = []
for i in range(129):
links.append(
trace_sdk._Span(
name="span{}".format(i),
context=trace_api.INVALID_SPAN_CONTEXT,
attributes=attributes,
)
)
span = trace_sdk._Span(
limits=trace_sdk.SpanLimits(),
name="span",
resource=Resource(
attributes=attributes,
),
context=trace_api.SpanContext(
trace_id=0x000000000000000000000000DEADBEEF,
span_id=0x00000000DEADBEF0,
is_remote=False,
),
links=links,
attributes=attributes,
)

span.start()
for i in range(131):
span.add_event("event{}".format(i), attributes=attributes)
span.end()
return span