Skip to content

Commit

Permalink
Add support for OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT env var
Browse files Browse the repository at this point in the history
Fixes #2051
  • Loading branch information
owais committed Aug 18, 2021
1 parent a60f030 commit ab2e367
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 104 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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"
"""
Expand Down
17 changes: 15 additions & 2 deletions opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand All @@ -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,
Expand All @@ -589,20 +593,28 @@ 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,
self.max_links,
self.max_event_attributes,
self.max_link_attributes,
self.max_attribute_length,
self.max_span_attribute_length,
)

@classmethod
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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:
Expand Down
217 changes: 122 additions & 95 deletions opentelemetry-sdk/tests/trace/test_trace.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 = (
Expand Down Expand Up @@ -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 = [
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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)

0 comments on commit ab2e367

Please sign in to comment.