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 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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased](https://github.com/open-telemetry/opentelemetry-python/compare/v1.3.0-0.22b0...HEAD)

### Added
- Dropped attributes/events/links count available exposed on ReadableSpans.
([#1893](https://github.com/open-telemetry/opentelemetry-python/pull/1893))
- Added dropped count to otlp, jaeger and zipkin exporters.
([#1893](https://github.com/open-telemetry/opentelemetry-python/pull/1893))

### Changed
- Updated `opentelemetry-opencensus-exporter` to use `service_name` of spans instead of resource
([#1897](https://github.com/open-telemetry/opentelemetry-python/pull/1897))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,24 @@ def _extract_tags(
if not span.status.is_ok:
translated.append(_get_bool_key_value("error", True))

if span.dropped_attributes:
translated.append(
_get_long_key_value(
"otel.dropped_attributes_count", span.dropped_attributes
)
)
if span.dropped_events:
translated.append(
_get_long_key_value(
"otel.dropped_events_count", span.dropped_events
)
)
if span.dropped_links:
translated.append(
_get_long_key_value(
"otel.dropped_links_count", span.dropped_links
)
)
return translated

def _extract_refs(
Expand Down Expand Up @@ -358,6 +376,15 @@ def _extract_logs(
if tag:
fields.append(tag)

if event.attributes.dropped:
fields.append(
_translate_attribute(
"otel.dropped_attributes_count",
event.attributes.dropped,
self._max_tag_value_length,
)
)

fields.append(
_get_string_key_value(
key="message",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,20 @@
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


def _translate_spans_with_dropped_attributes():
span = get_span_with_dropped_attributes_events_links()
translate = Translate([span])

# pylint: disable=protected-access
return translate._translate(pb_translator.ProtobufTranslator("svc"))


# pylint:disable=no-member
class TestJaegerExporter(unittest.TestCase):
def setUp(self):
Expand Down Expand Up @@ -475,8 +486,23 @@ def test_export_span_service_name(self):
exporter.export([span])
self.assertEqual(exporter.service_name, "test")


class MockResponse:
def __init__(self, status_code):
self.status_code = status_code
self.text = status_code
def test_dropped_span_attributes(self):
spans = _translate_spans_with_dropped_attributes()
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"])

def test_dropped_event_attributes(self):
spans = _translate_spans_with_dropped_attributes()
fields_by_keys = {
tag.key: tag.v_str or tag.v_int64
for tag in spans[0].logs[0].fields
}
# get events
self.assertEqual(
2,
fields_by_keys["otel.dropped_attributes_count"],
)
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ def _convert_int_to_i64(val):
return val


def _append_dropped(tags, key, val):
if val:
tags.append(_get_long_tag(key, val))


class Translator(abc.ABC):
def __init__(self, max_tag_value_length: Optional[int] = None):
self._max_tag_value_length = max_tag_value_length
Expand Down Expand Up @@ -181,7 +186,7 @@ def _translate_span(self, span: ReadableSpan) -> TCollector.Span:
return jaeger_span

def _extract_tags(self, span: ReadableSpan) -> Sequence[TCollector.Tag]:

# pylint: disable=too-many-branches
translated = []
if span.attributes:
for key, value in span.attributes.items():
Expand Down Expand Up @@ -226,6 +231,18 @@ def _extract_tags(self, span: ReadableSpan) -> Sequence[TCollector.Tag]:
if not span.status.is_ok:
translated.append(_get_bool_tag("error", True))

_append_dropped(
translated,
"otel.dropped_attributes_count",
span.dropped_attributes,
)
_append_dropped(
translated, "otel.dropped_events_count", span.dropped_events
)
_append_dropped(
translated, "otel.dropped_links_count", span.dropped_links
)

return translated

def _extract_refs(
Expand Down Expand Up @@ -269,6 +286,12 @@ def _extract_logs(
if tag:
fields.append(tag)

_append_dropped(
fields,
"otel.dropped_attributes_count",
event.attributes.dropped,
)

fields.append(
TCollector.Tag(
key="message",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,21 @@
from opentelemetry.sdk.resources import SERVICE_NAME
from opentelemetry.sdk.trace import Resource, TracerProvider
from opentelemetry.sdk.util.instrumentation import InstrumentationInfo
from opentelemetry.test.spantestutil import (
get_span_with_dropped_attributes_events_links,
)
from opentelemetry.trace import SpanKind
from opentelemetry.trace.status import Status, StatusCode


def _translate_spans_with_dropped_attributes():
span = get_span_with_dropped_attributes_events_links()
translate = Translate([span])

# pylint: disable=protected-access
return translate._translate(ThriftTranslator(max_tag_value_length=5))


class TestJaegerExporter(unittest.TestCase):
def setUp(self):
# create and save span to be used in tests
Expand Down Expand Up @@ -562,7 +573,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 @@ -579,12 +592,38 @@ 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_span_attributes(self):
spans = _translate_spans_with_dropped_attributes()
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_dropped_event_attributes(self):
spans = _translate_spans_with_dropped_attributes()
tags_by_keys = {
tag.key: tag.vLong
for tag in spans[0].logs[0].fields
if tag.vType == jaeger.TagType.LONG
}
self.assertEqual(
2,
tags_by_keys["otel.dropped_attributes_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 @@ -175,6 +175,7 @@ def _translate_events(self, sdk_span: ReadableSpan) -> None:
collector_span_event = CollectorSpan.Event(
name=sdk_span_event.name,
time_unix_nano=sdk_span_event.timestamp,
dropped_attributes_count=sdk_span_event.attributes.dropped,
)

for key, value in sdk_span_event.attributes.items():
Expand All @@ -201,6 +202,7 @@ def _translate_links(self, sdk_span: ReadableSpan) -> None:
sdk_span_link.context.trace_id.to_bytes(16, "big")
),
span_id=(sdk_span_link.context.span_id.to_bytes(8, "big")),
dropped_attributes_count=sdk_span_link.attributes.dropped,
)

for key, value in sdk_span_link.attributes.items():
Expand Down Expand Up @@ -272,6 +274,18 @@ def _translate_data(
self._translate_events(sdk_span)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe proto defines dropped_attributes_count nested inside Event and Link as well?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this still applies?

self._translate_links(sdk_span)
self._translate_status(sdk_span)
if sdk_span.dropped_attributes:
self._collector_span_kwargs[
"dropped_attributes_count"
] = sdk_span.dropped_attributes
if sdk_span.dropped_events:
self._collector_span_kwargs[
"dropped_events_count"
] = sdk_span.dropped_events
if sdk_span.dropped_links:
self._collector_span_kwargs[
"dropped_links_count"
] = sdk_span.dropped_links

self._collector_span_kwargs["kind"] = getattr(
CollectorSpan.SpanKind,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from google.rpc.error_details_pb2 import RetryInfo
from grpc import ChannelCredentials, Compression, StatusCode, server

from opentelemetry.attributes import BoundedAttributes
from opentelemetry.exporter.otlp.proto.grpc.exporter import (
_translate_key_values,
)
Expand Down Expand Up @@ -68,6 +69,9 @@
SpanExportResult,
)
from opentelemetry.sdk.util.instrumentation import InstrumentationInfo
from opentelemetry.test.spantestutil import (
get_span_with_dropped_attributes_events_links,
)

THIS_DIR = os.path.dirname(__file__)

Expand Down Expand Up @@ -134,7 +138,9 @@ def setUp(self):
event_mock = Mock(
**{
"timestamp": 1591240820506462784,
"attributes": OrderedDict([("a", 1), ("b", False)]),
"attributes": BoundedAttributes(
attributes={"a": 1, "b": False}
),
}
)

Expand All @@ -151,14 +157,16 @@ def setUp(self):
),
resource=SDKResource(OrderedDict([("a", 1), ("b", False)])),
parent=Mock(**{"span_id": 12345}),
attributes=OrderedDict([("a", 1), ("b", True)]),
attributes=BoundedAttributes(attributes={"a": 1, "b": True}),
events=[event_mock],
links=[
Mock(
**{
"context.trace_id": 1,
"context.span_id": 2,
"attributes": OrderedDict([("a", 1), ("b", False)]),
"attributes": BoundedAttributes(
attributes={"a": 1, "b": False}
),
"kind": OTLPSpan.SpanKind.SPAN_KIND_INTERNAL, # pylint: disable=no-member
}
)
Expand Down Expand Up @@ -601,6 +609,48 @@ def test_translate_key_values(self):
# self.assertEqual(kvlist_value.values[0].key, "asd")
# self.assertEqual(kvlist_value.values[0].value.string_value, "123")

def test_dropped_values(self):
span = get_span_with_dropped_attributes_events_links()
# pylint:disable=protected-access
translated = self.exporter._translate_data([span])
self.assertEqual(
1,
translated.resource_spans[0]
.instrumentation_library_spans[0]
.spans[0]
.dropped_links_count,
)
self.assertEqual(
2,
translated.resource_spans[0]
.instrumentation_library_spans[0]
.spans[0]
.dropped_attributes_count,
)
self.assertEqual(
3,
translated.resource_spans[0]
.instrumentation_library_spans[0]
.spans[0]
.dropped_events_count,
)
self.assertEqual(
2,
translated.resource_spans[0]
.instrumentation_library_spans[0]
.spans[0]
.links[0]
.dropped_attributes_count,
)
self.assertEqual(
2,
translated.resource_spans[0]
.instrumentation_library_spans[0]
.spans[0]
.events[0]
.dropped_attributes_count,
)


def _create_span_with_status(status: SDKStatus):
span = _Span(
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
Loading