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 17 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))

## [1.3.0-0.22b0](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.3.0-0.22b0) - 2021-06-01

### Added
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
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@
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 @@ -475,8 +478,15 @@ def test_export_span_service_name(self):
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])

class MockResponse:
def __init__(self, status_code):
self.status_code = status_code
self.text = status_code
# 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"])
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,21 @@ def _extract_tags(self, span: ReadableSpan) -> Sequence[TCollector.Tag]:
if not span.status.is_ok:
translated.append(_get_bool_tag("error", True))

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

return translated

def _extract_refs(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@
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

Expand Down Expand Up @@ -562,7 +565,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 +584,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 @@ -272,6 +272,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 @@ -68,6 +68,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 @@ -601,6 +604,32 @@ 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,
)


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
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,16 @@ 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"
]
annotations = {
annotation["key"]: annotation["value"]
for annotation in annotations
}
self.assertEqual("1", annotations["otel.dropped_links_count"])
self.assertEqual("2", annotations["otel.dropped_attributes_count"])
self.assertEqual("3", annotations["otel.dropped_events_count"])
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["otel.dropped_links_count"])
self.assertEqual("2", tags["otel.dropped_attributes_count"])
self.assertEqual("3", tags["otel.dropped_events_count"])
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
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 +235,16 @@ 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["otel.dropped_links_count"])
self.assertEqual("2", tags["otel.dropped_attributes_count"])
self.assertEqual("3", tags["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 @@ -318,6 +318,12 @@ def __init__(
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):
def wrapper(self, *args, **kwargs):
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
9 changes: 9 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,9 @@
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 +1447,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)
Loading