Skip to content

Commit

Permalink
Add support for OTEL_ATTRIBUTE_COUNT_LIMIT
Browse files Browse the repository at this point in the history
  • Loading branch information
owais committed Sep 23, 2021
1 parent 8b5a967 commit 5ee19b3
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,15 @@
Default: 512
"""

OTEL_ATTRIBUTE_COUNT_LIMIT = "OTEL_ATTRIBUTE_COUNT_LIMIT"
"""
.. envvar:: OTEL_ATTRIBUTE_COUNT_LIMIT
The :envvar:`OTEL_ATTRIBUTE_COUNT_LIMIT` represents the maximum allowed attribute count for spans, events and links.
This limit is overriden by model specific limits such as OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT.
Default: 128
"""

OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT = "OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT"
"""
.. envvar:: OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT
Expand Down
48 changes: 33 additions & 15 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_COUNT_LIMIT,
OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT,
OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT,
OTEL_LINK_ATTRIBUTE_COUNT_LIMIT,
Expand All @@ -61,11 +62,12 @@

logger = logging.getLogger(__name__)

_DEFAULT_OTEL_ATTRIBUTE_COUNT_LIMIT = 128
_DEFAULT_OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT = 128
_DEFAULT_OTEL_SPAN_EVENT_COUNT_LIMIT = 128
_DEFAULT_OTEL_SPAN_LINK_COUNT_LIMIT = 128
_DEFAULT_OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT = 128
_DEFAULT_OTEL_LINK_ATTRIBUTE_COUNT_LIMIT = 128
_DEFAULT_OTEL_SPAN_EVENT_COUNT_LIMIT = 128
_DEFAULT_OTEL_SPAN_LINK_COUNT_LIMIT = 128


_ENV_VALUE_UNSET = ""
Expand Down Expand Up @@ -533,19 +535,22 @@ class SpanLimits:
Limit precedence:
- If a model specific limit is set, it will be used.
- Else if the model specific limit has a default value, the default value will be used.
- Else if model specific limit has a corresponding global limit, the global limit will be used.
- Else if the model specific limit has a default value, the default value will be used.
Args:
max_attributes: Maximum number of attributes that can be added to a Span.
Environment variable: OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT
Default: {_DEFAULT_SPAN_ATTRIBUTE_COUNT_LIMIT}
Environment variable: OTEL_ATTRIBUTE_COUNT_LIMIT
Default: {_DEFAULT_ATTRIBUTE_COUNT_LIMIT}
max_events: Maximum number of events that can be added to a Span.
Environment variable: OTEL_SPAN_EVENT_COUNT_LIMIT
Default: {_DEFAULT_SPAN_EVENT_COUNT_LIMIT}
max_links: Maximum number of links that can be added to a Span.
Environment variable: OTEL_SPAN_LINK_COUNT_LIMIT
Default: {_DEFAULT_SPAN_LINK_COUNT_LIMIT}
max_span_attributes: Maximum number of attributes that can be added to a Span.
Environment variable: OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT
Default: {_DEFAULT_OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT}
max_event_attributes: Maximum number of attributes that can be added to an Event.
Default: {_DEFAULT_OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT}
max_link_attributes: Maximum number of attributes that can be added to a Link.
Expand All @@ -563,16 +568,14 @@ def __init__(
max_attributes: Optional[int] = None,
max_events: Optional[int] = None,
max_links: Optional[int] = None,
max_span_attributes: Optional[int] = None,
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,
OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT,
_DEFAULT_OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT,
)

# span events and links count
self.max_events = self._from_env_if_absent(
max_events,
OTEL_SPAN_EVENT_COUNT_LIMIT,
Expand All @@ -583,17 +586,31 @@ def __init__(
OTEL_SPAN_LINK_COUNT_LIMIT,
_DEFAULT_OTEL_SPAN_LINK_COUNT_LIMIT,
)

# attribute count
global_max_attributes = self._from_env_if_absent(max_attributes, OTEL_ATTRIBUTE_COUNT_LIMIT)
self.max_attributes = self._from_env_if_absent(
max_attributes,
OTEL_ATTRIBUTE_COUNT_LIMIT,
_DEFAULT_OTEL_ATTRIBUTE_COUNT_LIMIT,
)
self.max_span_attributes = self._from_env_if_absent(
max_span_attributes,
OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT,
global_max_attributes or _DEFAULT_OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT,
)
self.max_event_attributes = self._from_env_if_absent(
max_event_attributes,
OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT,
_DEFAULT_OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT,
global_max_attributes or _DEFAULT_OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT,
)
self.max_link_attributes = self._from_env_if_absent(
max_link_attributes,
OTEL_LINK_ATTRIBUTE_COUNT_LIMIT,
_DEFAULT_OTEL_LINK_ATTRIBUTE_COUNT_LIMIT,
global_max_attributes or _DEFAULT_OTEL_LINK_ATTRIBUTE_COUNT_LIMIT,
)

# attribute length
self.max_attribute_length = self._from_env_if_absent(
max_attribute_length,
OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT,
Expand All @@ -606,7 +623,7 @@ def __init__(
)

def __repr__(self):
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})"
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})"

@classmethod
def _from_env_if_absent(
Expand Down Expand Up @@ -641,13 +658,14 @@ def _from_env_if_absent(
max_attributes=SpanLimits.UNSET,
max_events=SpanLimits.UNSET,
max_links=SpanLimits.UNSET,
max_span_attributes=SpanLimits.UNSET,
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.
# not removed for backward compat. please use SpanLimits instead.
SPAN_ATTRIBUTE_COUNT_LIMIT = SpanLimits._from_env_if_absent(
None,
OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT,
Expand Down Expand Up @@ -717,7 +735,7 @@ def __init__(
self._limits = limits
self._lock = threading.Lock()
self._attributes = BoundedAttributes(
self._limits.max_attributes,
self._limits.max_span_attributes,
attributes,
immutable=False,
max_value_len=self._limits.max_span_attribute_length,
Expand Down
77 changes: 68 additions & 9 deletions opentelemetry-sdk/tests/trace/test_trace.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@
from opentelemetry.context import Context
from opentelemetry.sdk import resources, trace
from opentelemetry.sdk.environment_variables import (
OTEL_ATTRIBUTE_COUNT_LIMIT,
OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT,
OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT,
OTEL_LINK_ATTRIBUTE_COUNT_LIMIT,
OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT,
OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT,
OTEL_SPAN_EVENT_COUNT_LIMIT,
Expand Down Expand Up @@ -552,7 +555,7 @@ def test_surplus_span_links(self):

def test_surplus_span_attributes(self):
# pylint: disable=protected-access
max_attrs = trace.SpanLimits().max_attributes
max_attrs = trace.SpanLimits().max_span_attributes
attributes = {str(idx): idx for idx in range(0, 16 + max_attrs)}
tracer = new_tracer()
with tracer.start_as_current_span(
Expand Down Expand Up @@ -1325,8 +1328,20 @@ def test_limits_defaults(self):
limits = trace.SpanLimits()
self.assertEqual(
limits.max_attributes,
trace._DEFAULT_OTEL_ATTRIBUTE_COUNT_LIMIT,
)
self.assertEqual(
limits.max_span_attributes,
trace._DEFAULT_OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT,
)
self.assertEqual(
limits.max_event_attributes,
trace._DEFAULT_OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT,
)
self.assertEqual(
limits.max_link_attributes,
trace._DEFAULT_OTEL_LINK_ATTRIBUTE_COUNT_LIMIT,
)
self.assertEqual(
limits.max_events, trace._DEFAULT_OTEL_SPAN_EVENT_COUNT_LIMIT
)
Expand Down Expand Up @@ -1355,25 +1370,41 @@ def test_limits_attribute_length_limits_code(self):
self.assertEqual(limits.max_span_attribute_length, 33)

def test_limits_values_code(self):
max_attributes, max_events, max_links, max_attr_length = (
max_attributes, max_span_attributes, max_link_attributes, max_event_attributes, max_events, max_links, max_attr_length, max_span_attr_length = (
randint(0, 10000),
randint(0, 10000),
randint(0, 10000),
randint(0, 10000),
randint(0, 10000),
randint(0, 10000),
randint(0, 10000),
randint(0, 10000),
)
limits = trace.SpanLimits(
max_attributes=max_attributes,
max_events=max_events,
max_links=max_links,
max_attributes=max_attributes,
max_span_attributes=max_span_attributes,
max_event_attributes=max_event_attributes,
max_link_attributes=max_link_attributes,
max_attribute_length=max_attr_length,
max_span_attribute_length=max_span_attr_length,
)
self.assertEqual(limits.max_attributes, max_attributes)
self.assertEqual(limits.max_events, max_events)
self.assertEqual(limits.max_links, max_links)
self.assertEqual(limits.max_attributes, max_attributes)
self.assertEqual(limits.max_span_attributes, max_span_attributes)
self.assertEqual(limits.max_event_attributes, max_event_attributes)
self.assertEqual(limits.max_link_attributes, max_link_attributes)
self.assertEqual(limits.max_attribute_length, max_attr_length)
self.assertEqual(limits.max_span_attribute_length, max_span_attr_length)

def test_limits_values_env(self):
max_attributes, max_events, max_links, max_attr_length = (
max_attributes, max_span_attributes, max_link_attributes, max_event_attributes, max_events, max_links, max_attr_length, max_span_attr_length = (
randint(0, 10000),
randint(0, 10000),
randint(0, 10000),
randint(0, 10000),
randint(0, 10000),
randint(0, 10000),
randint(0, 10000),
Expand All @@ -1382,16 +1413,25 @@ def test_limits_values_env(self):
with mock.patch.dict(
"os.environ",
{
OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT: str(max_attributes),
OTEL_ATTRIBUTE_COUNT_LIMIT: str(max_attributes),
OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT: str(max_span_attributes),
OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT: str(max_event_attributes),
OTEL_LINK_ATTRIBUTE_COUNT_LIMIT: str(max_link_attributes),
OTEL_SPAN_EVENT_COUNT_LIMIT: str(max_events),
OTEL_SPAN_LINK_COUNT_LIMIT: str(max_links),
OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT: str(max_attr_length),
OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT: str(max_attr_length),
OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT: str(max_span_attr_length),
},
):
limits = trace.SpanLimits()
self.assertEqual(limits.max_attributes, max_attributes)
self.assertEqual(limits.max_events, max_events)
self.assertEqual(limits.max_links, max_links)
self.assertEqual(limits.max_attributes, max_attributes)
self.assertEqual(limits.max_span_attributes, max_span_attributes)
self.assertEqual(limits.max_event_attributes, max_event_attributes)
self.assertEqual(limits.max_link_attributes, max_link_attributes)
self.assertEqual(limits.max_attribute_length, max_attr_length)
self.assertEqual(limits.max_span_attribute_length, max_span_attr_length)

@mock.patch.dict(
"os.environ",
Expand All @@ -1413,6 +1453,25 @@ def test_span_limits_env(self):
max_span_attr_len=15,
)

@mock.patch.dict(
"os.environ",
{
OTEL_ATTRIBUTE_COUNT_LIMIT: "13",
OTEL_SPAN_EVENT_COUNT_LIMIT: "7",
OTEL_SPAN_LINK_COUNT_LIMIT: "4",
OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT: "11",
},
)
def test_span_limits_global_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=11,
)

@mock.patch.dict(
"os.environ",
{
Expand Down Expand Up @@ -1475,7 +1534,7 @@ def test_span_no_limits_code(self):
self._test_span_no_limits(
new_tracer(
span_limits=trace.SpanLimits(
max_attributes=trace.SpanLimits.UNSET,
max_span_attributes=trace.SpanLimits.UNSET,
max_links=trace.SpanLimits.UNSET,
max_events=trace.SpanLimits.UNSET,
max_attribute_length=trace.SpanLimits.UNSET,
Expand Down

0 comments on commit 5ee19b3

Please sign in to comment.