From ab2e3675c186291b84009b3eaf63a402a49018e2 Mon Sep 17 00:00:00 2001 From: Owais Lone Date: Thu, 19 Aug 2021 04:12:07 +0530 Subject: [PATCH] Add support for `OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT` env var Fixes #2051 --- .../sdk/environment_variables/__init__.py | 14 +- .../src/opentelemetry/sdk/trace/__init__.py | 17 +- opentelemetry-sdk/tests/trace/test_trace.py | 217 ++++++++++-------- 3 files changed, 144 insertions(+), 104 deletions(-) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/environment_variables/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/environment_variables/__init__.py index 63a561d7fa7..d6af7a57095 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/environment_variables/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/environment_variables/__init__.py @@ -98,6 +98,13 @@ Default: 512 """ +OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT = "OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT" +""" +.. envvar:: OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT + +The :envvar:`OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT` represents the maximum allowed attribute length. +""" + OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT = "OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT" """ .. envvar:: OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT @@ -113,13 +120,6 @@ The :envvar:`OTEL_LINK_ATTRIBUTE_COUNT_LIMIT` represents the maximum allowed link attribute count. Default: 128 """ -OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT = "OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT" -""" -.. envvar:: OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT - -The :envvar:`OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT` represents the maximum allowed attribute length. -Default: 128 -""" OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT = "OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT" """ diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py index 60319e1bfa8..8f7b035f218 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py @@ -41,6 +41,7 @@ from opentelemetry.attributes import BoundedAttributes from opentelemetry.sdk import util from opentelemetry.sdk.environment_variables import ( + OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT, OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT, OTEL_LINK_ATTRIBUTE_COUNT_LIMIT, OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT, @@ -551,6 +552,8 @@ class SpanLimits: Default: {_DEFAULT_OTEL_LINK_ATTRIBUTE_COUNT_LIMIT} max_attribute_length: Maximum length an attribute value can have. Values longer than the specified length will be truncated. + max_span_attribute_length: Maximum length a span attribute value can have. Values longer than + the specified length will be truncated. """ UNSET = -1 @@ -563,6 +566,7 @@ def __init__( max_event_attributes: Optional[int] = None, max_link_attributes: Optional[int] = None, max_attribute_length: Optional[int] = None, + max_span_attribute_length: Optional[int] = None, ): self.max_attributes = self._from_env_if_absent( max_attributes, @@ -589,13 +593,20 @@ def __init__( OTEL_LINK_ATTRIBUTE_COUNT_LIMIT, _DEFAULT_OTEL_LINK_ATTRIBUTE_COUNT_LIMIT, ) + self.max_attribute_length = self._from_env_if_absent( max_attribute_length, + OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT, + ) + self.max_span_attribute_length = self._from_env_if_absent( + max_span_attribute_length, OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT, + # use global attribute length limit as default + self.max_attribute_length, ) def __repr__(self): - return "{}(max_attributes={}, max_events={}, max_links={}, max_event_attributes={}, max_link_attributes={}, max_attribute_length={})".format( + return "{}(max_attributes={}, max_events={}, max_links={}, max_event_attributes={}, max_link_attributes={}, max_attribute_length={}, max_span_attribute_length={})".format( type(self).__name__, self.max_attributes, self.max_events, @@ -603,6 +614,7 @@ def __repr__(self): self.max_event_attributes, self.max_link_attributes, self.max_attribute_length, + self.max_span_attribute_length, ) @classmethod @@ -638,6 +650,7 @@ def _from_env_if_absent( max_event_attributes=SpanLimits.UNSET, max_link_attributes=SpanLimits.UNSET, max_attribute_length=SpanLimits.UNSET, + max_span_attribute_length=SpanLimits.UNSET, ) # not remove for backward compat. please use SpanLimits instead. @@ -713,7 +726,7 @@ def __init__( self._limits.max_attributes, attributes, immutable=False, - max_value_len=self._limits.max_attribute_length, + max_value_len=self._limits.max_span_attribute_length, ) self._events = self._new_events() if events: diff --git a/opentelemetry-sdk/tests/trace/test_trace.py b/opentelemetry-sdk/tests/trace/test_trace.py index 29609184d25..271af682e10 100644 --- a/opentelemetry-sdk/tests/trace/test_trace.py +++ b/opentelemetry-sdk/tests/trace/test_trace.py @@ -26,6 +26,7 @@ from opentelemetry.context import Context from opentelemetry.sdk import resources, trace from opentelemetry.sdk.environment_variables import ( + OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT, OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT, OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT, OTEL_SPAN_EVENT_COUNT_LIMIT, @@ -1335,6 +1336,23 @@ def test_limits_defaults(self): limits.max_links, trace._DEFAULT_OTEL_SPAN_LINK_COUNT_LIMIT ) self.assertIsNone(limits.max_attribute_length) + self.assertIsNone(limits.max_span_attribute_length) + + def test_limits_attribute_length_limits_code(self): + # global limit unset while span limit is set + limits = trace.SpanLimits(max_span_attribute_length=22) + self.assertIsNone(limits.max_attribute_length) + self.assertEqual(limits.max_span_attribute_length, 22) + + # span limit falls back to global limit when no value is provided + limits = trace.SpanLimits(max_attribute_length=22) + self.assertEqual(limits.max_attribute_length, 22) + self.assertEqual(limits.max_span_attribute_length, 22) + + # global and span limits set to different values + limits = trace.SpanLimits(max_attribute_length=22, max_span_attribute_length=33) + self.assertEqual(limits.max_attribute_length, 22) + self.assertEqual(limits.max_span_attribute_length, 33) def test_limits_values_code(self): max_attributes, max_events, max_links, max_attr_length = ( @@ -1375,8 +1393,109 @@ def test_limits_values_env(self): self.assertEqual(limits.max_events, max_events) self.assertEqual(limits.max_links, max_links) + + @mock.patch.dict( + "os.environ", + { + OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT: "13", + OTEL_SPAN_EVENT_COUNT_LIMIT: "7", + OTEL_SPAN_LINK_COUNT_LIMIT: "4", + OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT: "11", + OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT: "15", + }, + ) + def test_span_limits_env(self): + self._test_span_limits( + new_tracer(), + max_attrs=13, + max_events=7, + max_links=4, + max_attr_len=11, + max_span_attr_len=15, + ) + + @mock.patch.dict( + "os.environ", + { + OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT: "10", + OTEL_SPAN_EVENT_COUNT_LIMIT: "20", + OTEL_SPAN_LINK_COUNT_LIMIT: "30", + OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT: "40", + OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT: "50", + }, + ) + def test_span_limits_default_to_env(self): + self._test_span_limits( + new_tracer( + span_limits=trace.SpanLimits( + max_attributes=None, + max_events=None, + max_links=None, + max_attribute_length=None, + max_span_attribute_length=None, + ) + ), + max_attrs=10, + max_events=20, + max_links=30, + max_attr_len=40, + max_span_attr_len=50, + ) + + def test_span_limits_code(self): + self._test_span_limits( + new_tracer( + span_limits=trace.SpanLimits( + max_attributes=11, + max_events=15, + max_links=13, + max_attribute_length=9, + max_span_attribute_length=25, + ) + ), + max_attrs=11, + max_events=15, + max_links=13, + max_attr_len=9, + max_span_attr_len=25, + ) + + @mock.patch.dict( + "os.environ", + { + OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT: "unset", + OTEL_SPAN_EVENT_COUNT_LIMIT: "unset", + OTEL_SPAN_LINK_COUNT_LIMIT: "unset", + OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT: "unset", + }, + ) + + def test_span_no_limits_env(self): + self._test_span_no_limits(new_tracer()) + + def test_span_no_limits_code(self): + self._test_span_no_limits( + new_tracer( + span_limits=trace.SpanLimits( + max_attributes=trace.SpanLimits.UNSET, + max_links=trace.SpanLimits.UNSET, + max_events=trace.SpanLimits.UNSET, + max_attribute_length=trace.SpanLimits.UNSET, + ) + ) + ) + + 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) + self.assertEqual(2, span.events[0].attributes.dropped) + self.assertEqual(2, span.links[0].attributes.dropped) + self.assertEqual(2, span.resource.attributes.dropped) + def _test_span_limits( - self, tracer, max_attrs, max_events, max_links, max_attr_len + self, tracer, max_attrs, max_events, max_links, max_attr_len, max_span_attr_len, ): id_generator = RandomIdGenerator() some_links = [ @@ -1426,7 +1545,7 @@ def _test_span_limits( self._assert_attr_length(attr_val, max_attr_len) for attr_val in root.attributes.values(): - self._assert_attr_length(attr_val, max_attr_len) + self._assert_attr_length(attr_val, max_span_attr_len) def _test_span_no_limits(self, tracer): num_links = int(trace._DEFAULT_OTEL_SPAN_LINK_COUNT_LIMIT) + randint( @@ -1469,96 +1588,4 @@ def _test_span_no_limits(self, tracer): self.assertEqual(len(root.attributes), num_attributes) for attr_val in root.attributes.values(): - self.assertEqual(attr_val, self.long_val) - - @mock.patch.dict( - "os.environ", - { - OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT: "13", - OTEL_SPAN_EVENT_COUNT_LIMIT: "7", - OTEL_SPAN_LINK_COUNT_LIMIT: "4", - OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT: "11", - }, - ) - def test_span_limits_env(self): - self._test_span_limits( - new_tracer(), - max_attrs=13, - max_events=7, - max_links=4, - max_attr_len=11, - ) - - @mock.patch.dict( - "os.environ", - { - OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT: "10", - OTEL_SPAN_EVENT_COUNT_LIMIT: "20", - OTEL_SPAN_LINK_COUNT_LIMIT: "30", - OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT: "40", - }, - ) - def test_span_limits_default_to_env(self): - self._test_span_limits( - new_tracer( - span_limits=trace.SpanLimits( - max_attributes=None, - max_events=None, - max_links=None, - max_attribute_length=None, - ) - ), - max_attrs=10, - max_events=20, - max_links=30, - max_attr_len=40, - ) - - def test_span_limits_code(self): - self._test_span_limits( - new_tracer( - span_limits=trace.SpanLimits( - max_attributes=11, - max_events=15, - max_links=13, - max_attribute_length=9, - ) - ), - max_attrs=11, - max_events=15, - max_links=13, - max_attr_len=9, - ) - - @mock.patch.dict( - "os.environ", - { - OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT: "unset", - OTEL_SPAN_EVENT_COUNT_LIMIT: "unset", - OTEL_SPAN_LINK_COUNT_LIMIT: "unset", - OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT: "unset", - }, - ) - def test_span_no_limits_env(self): - self._test_span_no_limits(new_tracer()) - - def test_span_no_limits_code(self): - self._test_span_no_limits( - new_tracer( - span_limits=trace.SpanLimits( - max_attributes=trace.SpanLimits.UNSET, - max_links=trace.SpanLimits.UNSET, - max_events=trace.SpanLimits.UNSET, - max_attribute_length=trace.SpanLimits.UNSET, - ) - ) - ) - - 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) - self.assertEqual(2, span.events[0].attributes.dropped) - self.assertEqual(2, span.links[0].attributes.dropped) - self.assertEqual(2, span.resource.attributes.dropped) + self.assertEqual(attr_val, self.long_val) \ No newline at end of file