Skip to content

Commit e6d4b9c

Browse files
owaissrikanthccv
andauthored
Add support for OTEL_ATTRIBUTE_COUNT_LIMIT (#2139)
* Add support for OTEL_ATTRIBUTE_COUNT_LIMIT Fixes #2055 Fixes #2111 * Update opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py Co-authored-by: Srikanth Chekuri <[email protected]> Co-authored-by: Srikanth Chekuri <[email protected]>
1 parent b2d5ab3 commit e6d4b9c

File tree

5 files changed

+140
-25
lines changed

5 files changed

+140
-25
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
([#2101](https://github.com/open-telemetry/opentelemetry-python/pull/2101))
2121
- Fix incorrect headers parsing via environment variables
2222
([#2103](https://github.com/open-telemetry/opentelemetry-python/pull/2103))
23+
- Add support for OTEL_ATTRIBUTE_COUNT_LIMIT
24+
([#2139](https://github.com/open-telemetry/opentelemetry-python/pull/2139))
2325
- Attribute limits no longer apply to Resource attributes
2426
([#2138](https://github.com/open-telemetry/opentelemetry-python/pull/2138))
2527
- `opentelemetry-exporter-otlp`: Add `opentelemetry-otlp-proto-http` as dependency

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

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

101+
OTEL_ATTRIBUTE_COUNT_LIMIT = "OTEL_ATTRIBUTE_COUNT_LIMIT"
102+
"""
103+
.. envvar:: OTEL_ATTRIBUTE_COUNT_LIMIT
104+
105+
The :envvar:`OTEL_ATTRIBUTE_COUNT_LIMIT` represents the maximum allowed attribute count for spans, events and links.
106+
This limit is overriden by model specific limits such as OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT.
107+
Default: 128
108+
"""
109+
101110
OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT = "OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT"
102111
"""
103112
.. envvar:: OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT

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

+36-16
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_COUNT_LIMIT,
4445
OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT,
4546
OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT,
4647
OTEL_LINK_ATTRIBUTE_COUNT_LIMIT,
@@ -61,11 +62,12 @@
6162

6263
logger = logging.getLogger(__name__)
6364

65+
_DEFAULT_OTEL_ATTRIBUTE_COUNT_LIMIT = 128
6466
_DEFAULT_OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT = 128
65-
_DEFAULT_OTEL_SPAN_EVENT_COUNT_LIMIT = 128
66-
_DEFAULT_OTEL_SPAN_LINK_COUNT_LIMIT = 128
6767
_DEFAULT_OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT = 128
6868
_DEFAULT_OTEL_LINK_ATTRIBUTE_COUNT_LIMIT = 128
69+
_DEFAULT_OTEL_SPAN_EVENT_COUNT_LIMIT = 128
70+
_DEFAULT_OTEL_SPAN_LINK_COUNT_LIMIT = 128
6971

7072

7173
_ENV_VALUE_UNSET = ""
@@ -533,19 +535,23 @@ class SpanLimits:
533535
Limit precedence:
534536
535537
- If a model specific limit is set, it will be used.
538+
- Else if the corresponding global limit is set, it will be used.
536539
- Else if the model specific limit has a default value, the default value will be used.
537-
- Else if model specific limit has a corresponding global limit, the global limit will be used.
540+
- Else if the global limit has a default value, the default value will be used.
538541
539542
Args:
540-
max_attributes: Maximum number of attributes that can be added to a Span.
541-
Environment variable: OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT
542-
Default: {_DEFAULT_SPAN_ATTRIBUTE_COUNT_LIMIT}
543+
max_attributes: Maximum number of attributes that can be added to a span, event, and link.
544+
Environment variable: OTEL_ATTRIBUTE_COUNT_LIMIT
545+
Default: {_DEFAULT_ATTRIBUTE_COUNT_LIMIT}
543546
max_events: Maximum number of events that can be added to a Span.
544547
Environment variable: OTEL_SPAN_EVENT_COUNT_LIMIT
545548
Default: {_DEFAULT_SPAN_EVENT_COUNT_LIMIT}
546549
max_links: Maximum number of links that can be added to a Span.
547550
Environment variable: OTEL_SPAN_LINK_COUNT_LIMIT
548551
Default: {_DEFAULT_SPAN_LINK_COUNT_LIMIT}
552+
max_span_attributes: Maximum number of attributes that can be added to a Span.
553+
Environment variable: OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT
554+
Default: {_DEFAULT_OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT}
549555
max_event_attributes: Maximum number of attributes that can be added to an Event.
550556
Default: {_DEFAULT_OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT}
551557
max_link_attributes: Maximum number of attributes that can be added to a Link.
@@ -563,16 +569,14 @@ def __init__(
563569
max_attributes: Optional[int] = None,
564570
max_events: Optional[int] = None,
565571
max_links: Optional[int] = None,
572+
max_span_attributes: Optional[int] = None,
566573
max_event_attributes: Optional[int] = None,
567574
max_link_attributes: Optional[int] = None,
568575
max_attribute_length: Optional[int] = None,
569576
max_span_attribute_length: Optional[int] = None,
570577
):
571-
self.max_attributes = self._from_env_if_absent(
572-
max_attributes,
573-
OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT,
574-
_DEFAULT_OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT,
575-
)
578+
579+
# span events and links count
576580
self.max_events = self._from_env_if_absent(
577581
max_events,
578582
OTEL_SPAN_EVENT_COUNT_LIMIT,
@@ -583,17 +587,32 @@ def __init__(
583587
OTEL_SPAN_LINK_COUNT_LIMIT,
584588
_DEFAULT_OTEL_SPAN_LINK_COUNT_LIMIT,
585589
)
590+
591+
# attribute count
592+
global_max_attributes = self._from_env_if_absent(
593+
max_attributes, OTEL_ATTRIBUTE_COUNT_LIMIT
594+
)
595+
self.max_attributes = (
596+
global_max_attributes or _DEFAULT_OTEL_ATTRIBUTE_COUNT_LIMIT
597+
)
598+
599+
self.max_span_attributes = self._from_env_if_absent(
600+
max_span_attributes,
601+
OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT,
602+
global_max_attributes or _DEFAULT_OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT,
603+
)
586604
self.max_event_attributes = self._from_env_if_absent(
587605
max_event_attributes,
588606
OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT,
589-
_DEFAULT_OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT,
607+
global_max_attributes or _DEFAULT_OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT,
590608
)
591609
self.max_link_attributes = self._from_env_if_absent(
592610
max_link_attributes,
593611
OTEL_LINK_ATTRIBUTE_COUNT_LIMIT,
594-
_DEFAULT_OTEL_LINK_ATTRIBUTE_COUNT_LIMIT,
612+
global_max_attributes or _DEFAULT_OTEL_LINK_ATTRIBUTE_COUNT_LIMIT,
595613
)
596614

615+
# attribute length
597616
self.max_attribute_length = self._from_env_if_absent(
598617
max_attribute_length,
599618
OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT,
@@ -606,7 +625,7 @@ def __init__(
606625
)
607626

608627
def __repr__(self):
609-
return f"{type(self).__name__}(max_span_attributes={self.max_span_attribute_length}, max_events_attributes={self.max_event_attributes}, max_link_attributes={self.max_link_attributes}, max_attributes={self.max_attributes}, max_events={self.max_events}, max_links={self.max_links}, max_attribute_length={self.max_attribute_length})"
628+
return f"{type(self).__name__}(max_span_attributes={self.max_span_attributes}, max_events_attributes={self.max_event_attributes}, max_link_attributes={self.max_link_attributes}, max_attributes={self.max_attributes}, max_events={self.max_events}, max_links={self.max_links}, max_attribute_length={self.max_attribute_length})"
610629

611630
@classmethod
612631
def _from_env_if_absent(
@@ -641,13 +660,14 @@ def _from_env_if_absent(
641660
max_attributes=SpanLimits.UNSET,
642661
max_events=SpanLimits.UNSET,
643662
max_links=SpanLimits.UNSET,
663+
max_span_attributes=SpanLimits.UNSET,
644664
max_event_attributes=SpanLimits.UNSET,
645665
max_link_attributes=SpanLimits.UNSET,
646666
max_attribute_length=SpanLimits.UNSET,
647667
max_span_attribute_length=SpanLimits.UNSET,
648668
)
649669

650-
# not remove for backward compat. please use SpanLimits instead.
670+
# not removed for backward compat. please use SpanLimits instead.
651671
SPAN_ATTRIBUTE_COUNT_LIMIT = SpanLimits._from_env_if_absent(
652672
None,
653673
OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT,
@@ -717,7 +737,7 @@ def __init__(
717737
self._limits = limits
718738
self._lock = threading.Lock()
719739
self._attributes = BoundedAttributes(
720-
self._limits.max_attributes,
740+
self._limits.max_span_attributes,
721741
attributes,
722742
immutable=False,
723743
max_value_len=self._limits.max_span_attribute_length,

opentelemetry-sdk/tests/trace/test_trace.py

+92-9
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@
2626
from opentelemetry.context import Context
2727
from opentelemetry.sdk import resources, trace
2828
from opentelemetry.sdk.environment_variables import (
29+
OTEL_ATTRIBUTE_COUNT_LIMIT,
2930
OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT,
31+
OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT,
32+
OTEL_LINK_ATTRIBUTE_COUNT_LIMIT,
3033
OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT,
3134
OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT,
3235
OTEL_SPAN_EVENT_COUNT_LIMIT,
@@ -552,7 +555,7 @@ def test_surplus_span_links(self):
552555

553556
def test_surplus_span_attributes(self):
554557
# pylint: disable=protected-access
555-
max_attrs = trace.SpanLimits().max_attributes
558+
max_attrs = trace.SpanLimits().max_span_attributes
556559
attributes = {str(idx): idx for idx in range(0, 16 + max_attrs)}
557560
tracer = new_tracer()
558561
with tracer.start_as_current_span(
@@ -1325,8 +1328,20 @@ def test_limits_defaults(self):
13251328
limits = trace.SpanLimits()
13261329
self.assertEqual(
13271330
limits.max_attributes,
1331+
trace._DEFAULT_OTEL_ATTRIBUTE_COUNT_LIMIT,
1332+
)
1333+
self.assertEqual(
1334+
limits.max_span_attributes,
13281335
trace._DEFAULT_OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT,
13291336
)
1337+
self.assertEqual(
1338+
limits.max_event_attributes,
1339+
trace._DEFAULT_OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT,
1340+
)
1341+
self.assertEqual(
1342+
limits.max_link_attributes,
1343+
trace._DEFAULT_OTEL_LINK_ATTRIBUTE_COUNT_LIMIT,
1344+
)
13301345
self.assertEqual(
13311346
limits.max_events, trace._DEFAULT_OTEL_SPAN_EVENT_COUNT_LIMIT
13321347
)
@@ -1355,25 +1370,61 @@ def test_limits_attribute_length_limits_code(self):
13551370
self.assertEqual(limits.max_span_attribute_length, 33)
13561371

13571372
def test_limits_values_code(self):
1358-
max_attributes, max_events, max_links, max_attr_length = (
1373+
(
1374+
max_attributes,
1375+
max_span_attributes,
1376+
max_link_attributes,
1377+
max_event_attributes,
1378+
max_events,
1379+
max_links,
1380+
max_attr_length,
1381+
max_span_attr_length,
1382+
) = (
1383+
randint(0, 10000),
1384+
randint(0, 10000),
1385+
randint(0, 10000),
1386+
randint(0, 10000),
13591387
randint(0, 10000),
13601388
randint(0, 10000),
13611389
randint(0, 10000),
13621390
randint(0, 10000),
13631391
)
13641392
limits = trace.SpanLimits(
1365-
max_attributes=max_attributes,
13661393
max_events=max_events,
13671394
max_links=max_links,
1395+
max_attributes=max_attributes,
1396+
max_span_attributes=max_span_attributes,
1397+
max_event_attributes=max_event_attributes,
1398+
max_link_attributes=max_link_attributes,
13681399
max_attribute_length=max_attr_length,
1400+
max_span_attribute_length=max_span_attr_length,
13691401
)
1370-
self.assertEqual(limits.max_attributes, max_attributes)
13711402
self.assertEqual(limits.max_events, max_events)
13721403
self.assertEqual(limits.max_links, max_links)
1404+
self.assertEqual(limits.max_attributes, max_attributes)
1405+
self.assertEqual(limits.max_span_attributes, max_span_attributes)
1406+
self.assertEqual(limits.max_event_attributes, max_event_attributes)
1407+
self.assertEqual(limits.max_link_attributes, max_link_attributes)
13731408
self.assertEqual(limits.max_attribute_length, max_attr_length)
1409+
self.assertEqual(
1410+
limits.max_span_attribute_length, max_span_attr_length
1411+
)
13741412

13751413
def test_limits_values_env(self):
1376-
max_attributes, max_events, max_links, max_attr_length = (
1414+
(
1415+
max_attributes,
1416+
max_span_attributes,
1417+
max_link_attributes,
1418+
max_event_attributes,
1419+
max_events,
1420+
max_links,
1421+
max_attr_length,
1422+
max_span_attr_length,
1423+
) = (
1424+
randint(0, 10000),
1425+
randint(0, 10000),
1426+
randint(0, 10000),
1427+
randint(0, 10000),
13771428
randint(0, 10000),
13781429
randint(0, 10000),
13791430
randint(0, 10000),
@@ -1382,16 +1433,29 @@ def test_limits_values_env(self):
13821433
with mock.patch.dict(
13831434
"os.environ",
13841435
{
1385-
OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT: str(max_attributes),
1436+
OTEL_ATTRIBUTE_COUNT_LIMIT: str(max_attributes),
1437+
OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT: str(max_span_attributes),
1438+
OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT: str(max_event_attributes),
1439+
OTEL_LINK_ATTRIBUTE_COUNT_LIMIT: str(max_link_attributes),
13861440
OTEL_SPAN_EVENT_COUNT_LIMIT: str(max_events),
13871441
OTEL_SPAN_LINK_COUNT_LIMIT: str(max_links),
1388-
OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT: str(max_attr_length),
1442+
OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT: str(max_attr_length),
1443+
OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT: str(
1444+
max_span_attr_length
1445+
),
13891446
},
13901447
):
13911448
limits = trace.SpanLimits()
1392-
self.assertEqual(limits.max_attributes, max_attributes)
13931449
self.assertEqual(limits.max_events, max_events)
13941450
self.assertEqual(limits.max_links, max_links)
1451+
self.assertEqual(limits.max_attributes, max_attributes)
1452+
self.assertEqual(limits.max_span_attributes, max_span_attributes)
1453+
self.assertEqual(limits.max_event_attributes, max_event_attributes)
1454+
self.assertEqual(limits.max_link_attributes, max_link_attributes)
1455+
self.assertEqual(limits.max_attribute_length, max_attr_length)
1456+
self.assertEqual(
1457+
limits.max_span_attribute_length, max_span_attr_length
1458+
)
13951459

13961460
@mock.patch.dict(
13971461
"os.environ",
@@ -1413,6 +1477,25 @@ def test_span_limits_env(self):
14131477
max_span_attr_len=15,
14141478
)
14151479

1480+
@mock.patch.dict(
1481+
"os.environ",
1482+
{
1483+
OTEL_ATTRIBUTE_COUNT_LIMIT: "13",
1484+
OTEL_SPAN_EVENT_COUNT_LIMIT: "7",
1485+
OTEL_SPAN_LINK_COUNT_LIMIT: "4",
1486+
OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT: "11",
1487+
},
1488+
)
1489+
def test_span_limits_global_env(self):
1490+
self._test_span_limits(
1491+
new_tracer(),
1492+
max_attrs=13,
1493+
max_events=7,
1494+
max_links=4,
1495+
max_attr_len=11,
1496+
max_span_attr_len=11,
1497+
)
1498+
14161499
@mock.patch.dict(
14171500
"os.environ",
14181501
{
@@ -1475,7 +1558,7 @@ def test_span_no_limits_code(self):
14751558
self._test_span_no_limits(
14761559
new_tracer(
14771560
span_limits=trace.SpanLimits(
1478-
max_attributes=trace.SpanLimits.UNSET,
1561+
max_span_attributes=trace.SpanLimits.UNSET,
14791562
max_links=trace.SpanLimits.UNSET,
14801563
max_events=trace.SpanLimits.UNSET,
14811564
max_attribute_length=trace.SpanLimits.UNSET,

pyproject.toml

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ exclude = '''
44
(
55
/( # generated files
66
.tox|
7+
venv|
78
exporter/opentelemetry-exporter-jaeger-proto-grpc/src/opentelemetry/exporter/jaeger/proto/grpc/gen|
89
exporter/opentelemetry-exporter-jaeger-thrift/src/opentelemetry/exporter/jaeger/thrift/gen|
910
exporter/opentelemetry-exporter-zipkin-proto-http/src/opentelemetry/exporter/zipkin/proto/http/v2/gen|

0 commit comments

Comments
 (0)