Skip to content

Commit 4250078

Browse files
authored
Add support for OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT env var (#2056)
* Added support for `OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT` Fixes #2045 Fixes #2043 Fixes #2042 Fixes #2041
1 parent b5704d5 commit 4250078

File tree

4 files changed

+161
-99
lines changed

4 files changed

+161
-99
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2020
([#2044](https://github.com/open-telemetry/opentelemetry-python/pull/2044))
2121
- `opentelemetry-sdk` Fixed bugs (#2041, #2042 & #2045) in Span Limits
2222
([#2044](https://github.com/open-telemetry/opentelemetry-python/pull/2044))
23+
- `opentelemetry-sdk` Add support for `OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT` env var
24+
([#2056](https://github.com/open-telemetry/opentelemetry-python/pull/2056))
2325
- `opentelemetry-sdk` Treat limit even vars set to empty values as unset/unlimited.
2426
([#2054](https://github.com/open-telemetry/opentelemetry-python/pull/2054))
2527
- `opentelemetry-api` Attribute keys must be non-empty strings.

opentelemetry-sdk/src/opentelemetry/sdk/environment_variables/__init__.py

+9-1
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,13 @@
9898
Default: 512
9999
"""
100100

101+
OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT = "OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT"
102+
"""
103+
.. envvar:: OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT
104+
105+
The :envvar:`OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT` represents the maximum allowed attribute length.
106+
"""
107+
101108
OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT = "OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT"
102109
"""
103110
.. envvar:: OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT
@@ -128,7 +135,8 @@
128135
"""
129136
.. envvar:: OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT
130137
131-
The :envvar:`OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT` represents the maximum allowed length attribute values can have.
138+
The :envvar:`OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT` represents the maximum allowed length
139+
span attribute values can have. This takes precedence over :envvar:`OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT`.
132140
"""
133141

134142
OTEL_SPAN_EVENT_COUNT_LIMIT = "OTEL_SPAN_EVENT_COUNT_LIMIT"

opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py

+23-4
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
from opentelemetry.attributes import BoundedAttributes
4242
from opentelemetry.sdk import util
4343
from opentelemetry.sdk.environment_variables import (
44+
OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT,
4445
OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT,
4546
OTEL_LINK_ATTRIBUTE_COUNT_LIMIT,
4647
OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT,
@@ -535,6 +536,12 @@ class SpanLimits:
535536
environment variable.
536537
- If the environment variable is not set, the default value, if any, will be used.
537538
539+
Limit precedence:
540+
541+
- If a model specific limit is set, it will be used.
542+
- Else if the model specific limit has a default value, the default value will be used.
543+
- Else if model specific limit has a corresponding global limit, the global limit will be used.
544+
538545
Args:
539546
max_attributes: Maximum number of attributes that can be added to a Span.
540547
Environment variable: OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT
@@ -551,6 +558,8 @@ class SpanLimits:
551558
Default: {_DEFAULT_OTEL_LINK_ATTRIBUTE_COUNT_LIMIT}
552559
max_attribute_length: Maximum length an attribute value can have. Values longer than
553560
the specified length will be truncated.
561+
max_span_attribute_length: Maximum length a span attribute value can have. Values longer than
562+
the specified length will be truncated.
554563
"""
555564

556565
UNSET = -1
@@ -563,6 +572,7 @@ def __init__(
563572
max_event_attributes: Optional[int] = None,
564573
max_link_attributes: Optional[int] = None,
565574
max_attribute_length: Optional[int] = None,
575+
max_span_attribute_length: Optional[int] = None,
566576
):
567577
self.max_attributes = self._from_env_if_absent(
568578
max_attributes,
@@ -589,19 +599,27 @@ def __init__(
589599
OTEL_LINK_ATTRIBUTE_COUNT_LIMIT,
590600
_DEFAULT_OTEL_LINK_ATTRIBUTE_COUNT_LIMIT,
591601
)
602+
592603
self.max_attribute_length = self._from_env_if_absent(
593604
max_attribute_length,
605+
OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT,
606+
)
607+
self.max_span_attribute_length = self._from_env_if_absent(
608+
max_span_attribute_length,
594609
OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT,
610+
# use global attribute length limit as default
611+
self.max_attribute_length,
595612
)
596613

597614
def __repr__(self):
598-
return "{}(max_attributes={}, max_events={}, max_links={}, max_event_attributes={}, max_link_attributes={}, max_attribute_length={})".format(
615+
return "{}(max_span_attributes={}, max_events_attributes={}, max_link_attributes={}, max_attributes={}, max_events={}, max_links={}, max_attribute_length={})".format(
599616
type(self).__name__,
617+
self.max_span_attribute_length,
618+
self.max_event_attributes,
619+
self.max_link_attributes,
600620
self.max_attributes,
601621
self.max_events,
602622
self.max_links,
603-
self.max_event_attributes,
604-
self.max_link_attributes,
605623
self.max_attribute_length,
606624
)
607625

@@ -641,6 +659,7 @@ def _from_env_if_absent(
641659
max_event_attributes=SpanLimits.UNSET,
642660
max_link_attributes=SpanLimits.UNSET,
643661
max_attribute_length=SpanLimits.UNSET,
662+
max_span_attribute_length=SpanLimits.UNSET,
644663
)
645664

646665
# not remove for backward compat. please use SpanLimits instead.
@@ -716,7 +735,7 @@ def __init__(
716735
self._limits.max_attributes,
717736
attributes,
718737
immutable=False,
719-
max_value_len=self._limits.max_attribute_length,
738+
max_value_len=self._limits.max_span_attribute_length,
720739
)
721740
self._events = self._new_events()
722741
if events:

opentelemetry-sdk/tests/trace/test_trace.py

+127-94
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
from opentelemetry.context import Context
2727
from opentelemetry.sdk import resources, trace
2828
from opentelemetry.sdk.environment_variables import (
29+
OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT,
2930
OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT,
3031
OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT,
3132
OTEL_SPAN_EVENT_COUNT_LIMIT,
@@ -1335,6 +1336,25 @@ def test_limits_defaults(self):
13351336
limits.max_links, trace._DEFAULT_OTEL_SPAN_LINK_COUNT_LIMIT
13361337
)
13371338
self.assertIsNone(limits.max_attribute_length)
1339+
self.assertIsNone(limits.max_span_attribute_length)
1340+
1341+
def test_limits_attribute_length_limits_code(self):
1342+
# global limit unset while span limit is set
1343+
limits = trace.SpanLimits(max_span_attribute_length=22)
1344+
self.assertIsNone(limits.max_attribute_length)
1345+
self.assertEqual(limits.max_span_attribute_length, 22)
1346+
1347+
# span limit falls back to global limit when no value is provided
1348+
limits = trace.SpanLimits(max_attribute_length=22)
1349+
self.assertEqual(limits.max_attribute_length, 22)
1350+
self.assertEqual(limits.max_span_attribute_length, 22)
1351+
1352+
# global and span limits set to different values
1353+
limits = trace.SpanLimits(
1354+
max_attribute_length=22, max_span_attribute_length=33
1355+
)
1356+
self.assertEqual(limits.max_attribute_length, 22)
1357+
self.assertEqual(limits.max_span_attribute_length, 33)
13381358

13391359
def test_limits_values_code(self):
13401360
max_attributes, max_events, max_links, max_attr_length = (
@@ -1375,8 +1395,113 @@ def test_limits_values_env(self):
13751395
self.assertEqual(limits.max_events, max_events)
13761396
self.assertEqual(limits.max_links, max_links)
13771397

1398+
@mock.patch.dict(
1399+
"os.environ",
1400+
{
1401+
OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT: "13",
1402+
OTEL_SPAN_EVENT_COUNT_LIMIT: "7",
1403+
OTEL_SPAN_LINK_COUNT_LIMIT: "4",
1404+
OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT: "11",
1405+
OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT: "15",
1406+
},
1407+
)
1408+
def test_span_limits_env(self):
1409+
self._test_span_limits(
1410+
new_tracer(),
1411+
max_attrs=13,
1412+
max_events=7,
1413+
max_links=4,
1414+
max_attr_len=11,
1415+
max_span_attr_len=15,
1416+
)
1417+
1418+
@mock.patch.dict(
1419+
"os.environ",
1420+
{
1421+
OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT: "10",
1422+
OTEL_SPAN_EVENT_COUNT_LIMIT: "20",
1423+
OTEL_SPAN_LINK_COUNT_LIMIT: "30",
1424+
OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT: "40",
1425+
OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT: "50",
1426+
},
1427+
)
1428+
def test_span_limits_default_to_env(self):
1429+
self._test_span_limits(
1430+
new_tracer(
1431+
span_limits=trace.SpanLimits(
1432+
max_attributes=None,
1433+
max_events=None,
1434+
max_links=None,
1435+
max_attribute_length=None,
1436+
max_span_attribute_length=None,
1437+
)
1438+
),
1439+
max_attrs=10,
1440+
max_events=20,
1441+
max_links=30,
1442+
max_attr_len=40,
1443+
max_span_attr_len=50,
1444+
)
1445+
1446+
def test_span_limits_code(self):
1447+
self._test_span_limits(
1448+
new_tracer(
1449+
span_limits=trace.SpanLimits(
1450+
max_attributes=11,
1451+
max_events=15,
1452+
max_links=13,
1453+
max_attribute_length=9,
1454+
max_span_attribute_length=25,
1455+
)
1456+
),
1457+
max_attrs=11,
1458+
max_events=15,
1459+
max_links=13,
1460+
max_attr_len=9,
1461+
max_span_attr_len=25,
1462+
)
1463+
1464+
@mock.patch.dict(
1465+
"os.environ",
1466+
{
1467+
OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT: "",
1468+
OTEL_SPAN_EVENT_COUNT_LIMIT: "",
1469+
OTEL_SPAN_LINK_COUNT_LIMIT: "",
1470+
OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT: "",
1471+
},
1472+
)
1473+
def test_span_no_limits_env(self):
1474+
self._test_span_no_limits(new_tracer())
1475+
1476+
def test_span_no_limits_code(self):
1477+
self._test_span_no_limits(
1478+
new_tracer(
1479+
span_limits=trace.SpanLimits(
1480+
max_attributes=trace.SpanLimits.UNSET,
1481+
max_links=trace.SpanLimits.UNSET,
1482+
max_events=trace.SpanLimits.UNSET,
1483+
max_attribute_length=trace.SpanLimits.UNSET,
1484+
)
1485+
)
1486+
)
1487+
1488+
def test_dropped_attributes(self):
1489+
span = get_span_with_dropped_attributes_events_links()
1490+
self.assertEqual(1, span.dropped_links)
1491+
self.assertEqual(2, span.dropped_attributes)
1492+
self.assertEqual(3, span.dropped_events)
1493+
self.assertEqual(2, span.events[0].attributes.dropped)
1494+
self.assertEqual(2, span.links[0].attributes.dropped)
1495+
self.assertEqual(2, span.resource.attributes.dropped)
1496+
13781497
def _test_span_limits(
1379-
self, tracer, max_attrs, max_events, max_links, max_attr_len
1498+
self,
1499+
tracer,
1500+
max_attrs,
1501+
max_events,
1502+
max_links,
1503+
max_attr_len,
1504+
max_span_attr_len,
13801505
):
13811506
id_generator = RandomIdGenerator()
13821507
some_links = [
@@ -1426,7 +1551,7 @@ def _test_span_limits(
14261551
self._assert_attr_length(attr_val, max_attr_len)
14271552

14281553
for attr_val in root.attributes.values():
1429-
self._assert_attr_length(attr_val, max_attr_len)
1554+
self._assert_attr_length(attr_val, max_span_attr_len)
14301555

14311556
def _test_span_no_limits(self, tracer):
14321557
num_links = int(trace._DEFAULT_OTEL_SPAN_LINK_COUNT_LIMIT) + randint(
@@ -1470,95 +1595,3 @@ def _test_span_no_limits(self, tracer):
14701595
self.assertEqual(len(root.attributes), num_attributes)
14711596
for attr_val in root.attributes.values():
14721597
self.assertEqual(attr_val, self.long_val)
1473-
1474-
@mock.patch.dict(
1475-
"os.environ",
1476-
{
1477-
OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT: "13",
1478-
OTEL_SPAN_EVENT_COUNT_LIMIT: "7",
1479-
OTEL_SPAN_LINK_COUNT_LIMIT: "4",
1480-
OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT: "11",
1481-
},
1482-
)
1483-
def test_span_limits_env(self):
1484-
self._test_span_limits(
1485-
new_tracer(),
1486-
max_attrs=13,
1487-
max_events=7,
1488-
max_links=4,
1489-
max_attr_len=11,
1490-
)
1491-
1492-
@mock.patch.dict(
1493-
"os.environ",
1494-
{
1495-
OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT: "10",
1496-
OTEL_SPAN_EVENT_COUNT_LIMIT: "20",
1497-
OTEL_SPAN_LINK_COUNT_LIMIT: "30",
1498-
OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT: "40",
1499-
},
1500-
)
1501-
def test_span_limits_default_to_env(self):
1502-
self._test_span_limits(
1503-
new_tracer(
1504-
span_limits=trace.SpanLimits(
1505-
max_attributes=None,
1506-
max_events=None,
1507-
max_links=None,
1508-
max_attribute_length=None,
1509-
)
1510-
),
1511-
max_attrs=10,
1512-
max_events=20,
1513-
max_links=30,
1514-
max_attr_len=40,
1515-
)
1516-
1517-
def test_span_limits_code(self):
1518-
self._test_span_limits(
1519-
new_tracer(
1520-
span_limits=trace.SpanLimits(
1521-
max_attributes=11,
1522-
max_events=15,
1523-
max_links=13,
1524-
max_attribute_length=9,
1525-
)
1526-
),
1527-
max_attrs=11,
1528-
max_events=15,
1529-
max_links=13,
1530-
max_attr_len=9,
1531-
)
1532-
1533-
@mock.patch.dict(
1534-
"os.environ",
1535-
{
1536-
OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT: "",
1537-
OTEL_SPAN_EVENT_COUNT_LIMIT: "",
1538-
OTEL_SPAN_LINK_COUNT_LIMIT: "",
1539-
OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT: "",
1540-
},
1541-
)
1542-
def test_span_no_limits_env(self):
1543-
self._test_span_no_limits(new_tracer())
1544-
1545-
def test_span_no_limits_code(self):
1546-
self._test_span_no_limits(
1547-
new_tracer(
1548-
span_limits=trace.SpanLimits(
1549-
max_attributes=trace.SpanLimits.UNSET,
1550-
max_links=trace.SpanLimits.UNSET,
1551-
max_events=trace.SpanLimits.UNSET,
1552-
max_attribute_length=trace.SpanLimits.UNSET,
1553-
)
1554-
)
1555-
)
1556-
1557-
def test_dropped_attributes(self):
1558-
span = get_span_with_dropped_attributes_events_links()
1559-
self.assertEqual(1, span.dropped_links)
1560-
self.assertEqual(2, span.dropped_attributes)
1561-
self.assertEqual(3, span.dropped_events)
1562-
self.assertEqual(2, span.events[0].attributes.dropped)
1563-
self.assertEqual(2, span.links[0].attributes.dropped)
1564-
self.assertEqual(2, span.resource.attributes.dropped)

0 commit comments

Comments
 (0)