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

Fix otlp exporter translating sequence types #1818

Merged
merged 8 commits into from
May 11, 2021
Merged
Show file tree
Hide file tree
Changes from 7 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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Moved `opentelemetry-instrumentation` to contrib repository.
([#1797](https://github.com/open-telemetry/opentelemetry-python/pull/1797))

### Changed
- Fixed sequence values in OTLP exporter not translating
([#1818](https://github.com/open-telemetry/opentelemetry-python/pull/1818))

## [1.1.0](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.1.0) - 2021-04-20

### Added
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,11 @@
ssl_channel_credentials,
)

from opentelemetry.proto.common.v1.common_pb2 import AnyValue, KeyValue
from opentelemetry.proto.common.v1.common_pb2 import (
AnyValue,
ArrayValue,
KeyValue,
)
from opentelemetry.proto.resource.v1.resource_pb2 import Resource
from opentelemetry.sdk.environment_variables import (
OTEL_EXPORTER_OTLP_CERTIFICATE,
Expand Down Expand Up @@ -81,7 +85,7 @@ def environ_to_compression(environ_key: str) -> Optional[Compression]:
return _ENVIRON_TO_COMPRESSION[environ_value]


def _translate_key_values(key: Text, value: Any) -> KeyValue:
def _translate_value(value: Any) -> KeyValue:

if isinstance(value, bool):
any_value = AnyValue(bool_value=value)
Expand All @@ -96,17 +100,30 @@ def _translate_key_values(key: Text, value: Any) -> KeyValue:
any_value = AnyValue(double_value=value)

elif isinstance(value, Sequence):
any_value = AnyValue(array_value=value)
any_value = AnyValue(
array_value=ArrayValue(values=[_translate_value(v) for v in value])
)

elif isinstance(value, Mapping):
any_value = AnyValue(kvlist_value=value)
# Tracing specs currently does not support Mapping type attributes
# elif isinstance(value, Mapping):
# any_value = AnyValue(
# kvlist_value=KeyValueList(
# values=[
# _translate_key_values(str(k), v) for k, v in value.items()
# ]
# )
# )
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this mean any spans containing a Mapping attribute will throw an exception below? I don't think we should do that if the API allows to create KV attributes (not sure if it does). May be we can warn and drop the attribute instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

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


else:
raise Exception(
"Invalid type {} of value {}".format(type(value), value)
)

return KeyValue(key=key, value=any_value)
return any_value


def _translate_key_values(key: Text, value: Any) -> KeyValue:
return KeyValue(key=key, value=_translate_value(value))


def get_resource_data(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
from google.rpc.error_details_pb2 import RetryInfo
from grpc import ChannelCredentials, Compression, StatusCode, server

from opentelemetry.exporter.otlp.proto.grpc.exporter import (
_translate_key_values,
)
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import (
OTLPSpanExporter,
)
Expand All @@ -35,6 +38,7 @@
)
from opentelemetry.proto.common.v1.common_pb2 import (
AnyValue,
ArrayValue,
InstrumentationLibrary,
KeyValue,
)
Expand Down Expand Up @@ -545,6 +549,58 @@ def test_span_status_translate(self):
Status.DEPRECATED_STATUS_CODE_UNKNOWN_ERROR,
)

# pylint:disable=no-member
Copy link
Contributor

Choose a reason for hiding this comment

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

this tests makes me think we should make use to pytest parametrization https://docs.pytest.org/en/6.2.x/parametrize.html

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It could be a possible improvement however it might be a bit complex due to the nested property checking that this specific test is doing. I can see it being useful for other tests that have code branches like this one though.

def test_translate_key_values(self):
bool_value = _translate_key_values("bool_type", False)
self.assertTrue(isinstance(bool_value, KeyValue))
self.assertEqual(bool_value.key, "bool_type")
self.assertTrue(isinstance(bool_value.value, AnyValue))
self.assertFalse(bool_value.value.bool_value)

str_value = _translate_key_values("str_type", "str")
self.assertTrue(isinstance(str_value, KeyValue))
self.assertEqual(str_value.key, "str_type")
self.assertTrue(isinstance(str_value.value, AnyValue))
self.assertEqual(str_value.value.string_value, "str")

int_value = _translate_key_values("int_type", 2)
self.assertTrue(isinstance(int_value, KeyValue))
self.assertEqual(int_value.key, "int_type")
self.assertTrue(isinstance(int_value.value, AnyValue))
self.assertEqual(int_value.value.int_value, 2)

double_value = _translate_key_values("double_type", 3.2)
self.assertTrue(isinstance(double_value, KeyValue))
self.assertEqual(double_value.key, "double_type")
self.assertTrue(isinstance(double_value.value, AnyValue))
self.assertEqual(double_value.value.double_value, 3.2)

seq_value = _translate_key_values("seq_type", ["asd", "123"])
self.assertTrue(isinstance(seq_value, KeyValue))
self.assertEqual(seq_value.key, "seq_type")
self.assertTrue(isinstance(seq_value.value, AnyValue))
self.assertTrue(isinstance(seq_value.value.array_value, ArrayValue))

arr_value = seq_value.value.array_value
self.assertTrue(isinstance(arr_value.values[0], AnyValue))
self.assertEqual(arr_value.values[0].string_value, "asd")
self.assertTrue(isinstance(arr_value.values[1], AnyValue))
self.assertEqual(arr_value.values[1].string_value, "123")

# Tracing specs currently does not support Mapping type attributes
# map_value = _translate_key_values(
# "map_type", {"asd": "123", "def": "456"}
# )
# self.assertTrue(isinstance(map_value, KeyValue))
# self.assertEqual(map_value.key, "map_type")
# self.assertTrue(isinstance(map_value.value, AnyValue))
# self.assertTrue(isinstance(map_value.value.kvlist_value, KeyValueList))

# kvlist_value = map_value.value.kvlist_value
# self.assertTrue(isinstance(kvlist_value.values[0], KeyValue))
# self.assertEqual(kvlist_value.values[0].key, "asd")
# self.assertEqual(kvlist_value.values[0].value.string_value, "123")


def _create_span_with_status(status: SDKStatus):
span = _Span(
Expand Down