51
51
from opentelemetry .sdk .util import BoundedDict , BoundedList
52
52
from opentelemetry .sdk .util .instrumentation import InstrumentationInfo
53
53
from opentelemetry .trace import SpanContext
54
- from opentelemetry .trace .propagation import SPAN_KEY
55
54
from opentelemetry .trace .status import Status , StatusCode
56
55
from opentelemetry .util import types
57
56
from opentelemetry .util ._time import _time_ns
58
57
59
58
logger = logging .getLogger (__name__ )
60
59
61
- SPAN_ATTRIBUTE_COUNT_LIMIT = int (
62
- environ .get (OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT , 128 )
63
- )
64
-
65
- _SPAN_EVENT_COUNT_LIMIT = int (environ .get (OTEL_SPAN_EVENT_COUNT_LIMIT , 128 ))
66
- _SPAN_LINK_COUNT_LIMIT = int (environ .get (OTEL_SPAN_LINK_COUNT_LIMIT , 128 ))
60
+ _DEFAULT_SPAN_EVENTS_LIMIT = 128
61
+ _DEFAULT_SPAN_LINKS_LIMIT = 128
62
+ _DEFAULT_SPAN_ATTRIBUTES_LIMIT = 128
67
63
_VALID_ATTR_VALUE_TYPES = (bool , str , int , float )
64
+
68
65
# pylint: disable=protected-access
69
66
_TRACE_SAMPLER = sampling ._get_from_env_or_default ()
70
67
@@ -564,6 +561,81 @@ def _format_links(links):
564
561
return f_links
565
562
566
563
564
+ class _Limits :
565
+ """The limits that should be enforce on recorded data such as events, links, attributes etc.
566
+
567
+ This class does not enforce any limits itself. It only provides an a way read limits from env,
568
+ default values and in future from user provided arguments.
569
+
570
+ All limit must be a either non-negative integers or None.
571
+ Setting a limit to ``None`` will not set an limits for that data type.
572
+
573
+ Args:
574
+ max_events: Maximum number of events that can be added to a Span.
575
+ max_links: Maximum number of links that can be added to a Span.
576
+ max_attributes: Maximum number of attributes that can be added to a Span.
577
+ """
578
+
579
+ max_attributes : int
580
+ max_events : int
581
+ max_links : int
582
+
583
+ def __init__ (
584
+ self ,
585
+ max_events : Optional [int ] = None ,
586
+ max_links : Optional [int ] = None ,
587
+ max_attributes : Optional [int ] = None ,
588
+ ):
589
+ self .max_attributes = max_attributes
590
+ self .max_events = max_events
591
+ self .max_links = max_links
592
+
593
+ def __repr__ (self ):
594
+ return "max_attributes={}, max_events={}, max_links={}" .format (
595
+ self .max_attributes , self .max_events , self .max_links
596
+ )
597
+
598
+ @classmethod
599
+ def _create_from_env (cls ):
600
+ return cls (
601
+ max_attributes = cls ._from_env (
602
+ OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT , _DEFAULT_SPAN_ATTRIBUTES_LIMIT
603
+ ),
604
+ max_events = cls ._from_env (
605
+ OTEL_SPAN_EVENT_COUNT_LIMIT , _DEFAULT_SPAN_EVENTS_LIMIT
606
+ ),
607
+ max_links = cls ._from_env (
608
+ OTEL_SPAN_LINK_COUNT_LIMIT , _DEFAULT_SPAN_LINKS_LIMIT
609
+ ),
610
+ )
611
+
612
+ @staticmethod
613
+ def _from_env (env_var : str , default : int ) -> Optional [int ]:
614
+ value = environ .get (env_var , "" ).strip ().lower ()
615
+ if not value :
616
+ return default
617
+ if value == "unset" :
618
+ return None
619
+
620
+ err_msg = "{0} must be a non-negative number but got {1}" .format (
621
+ env_var , value
622
+ )
623
+ try :
624
+ value = int (value )
625
+ except ValueError :
626
+ raise ValueError (err_msg )
627
+ if value < 0 :
628
+ raise ValueError (err_msg )
629
+ return value
630
+
631
+
632
+ _UnsetLimits = _Limits ()
633
+
634
+ SPAN_ATTRIBUTE_COUNT_LIMIT = _Limits ._from_env (
635
+ OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT , _DEFAULT_SPAN_ATTRIBUTES_LIMIT
636
+ )
637
+
638
+
567
639
class Span (trace_api .Span , ReadableSpan ):
568
640
"""See `opentelemetry.trace.Span`.
569
641
@@ -628,7 +700,7 @@ def __init__(
628
700
self ._attributes = self ._new_attributes ()
629
701
else :
630
702
self ._attributes = BoundedDict .from_map (
631
- SPAN_ATTRIBUTE_COUNT_LIMIT , attributes
703
+ self . _limits . max_attributes , attributes
632
704
)
633
705
634
706
self ._events = self ._new_events ()
@@ -644,24 +716,21 @@ def __init__(
644
716
if links is None :
645
717
self ._links = self ._new_links ()
646
718
else :
647
- self ._links = BoundedList .from_seq (_SPAN_LINK_COUNT_LIMIT , links )
719
+ self ._links = BoundedList .from_seq (self . _limits . max_links , links )
648
720
649
721
def __repr__ (self ):
650
722
return '{}(name="{}", context={})' .format (
651
723
type (self ).__name__ , self ._name , self ._context
652
724
)
653
725
654
- @staticmethod
655
- def _new_attributes ():
656
- return BoundedDict (SPAN_ATTRIBUTE_COUNT_LIMIT )
726
+ def _new_attributes (self ):
727
+ return BoundedDict (self ._limits .max_attributes )
657
728
658
- @staticmethod
659
- def _new_events ():
660
- return BoundedList (_SPAN_EVENT_COUNT_LIMIT )
729
+ def _new_events (self ):
730
+ return BoundedList (self ._limits .max_events )
661
731
662
- @staticmethod
663
- def _new_links ():
664
- return BoundedList (_SPAN_LINK_COUNT_LIMIT )
732
+ def _new_links (self ):
733
+ return BoundedList (self ._limits .max_links )
665
734
666
735
def get_span_context (self ):
667
736
return self ._context
@@ -834,6 +903,10 @@ class _Span(Span):
834
903
by other mechanisms than through the `Tracer`.
835
904
"""
836
905
906
+ def __init__ (self , * args , limits = _UnsetLimits , ** kwargs ):
907
+ self ._limits = limits
908
+ super ().__init__ (* args , ** kwargs )
909
+
837
910
838
911
class Tracer (trace_api .Tracer ):
839
912
"""See `opentelemetry.trace.Tracer`."""
@@ -853,6 +926,11 @@ def __init__(
853
926
self .span_processor = span_processor
854
927
self .id_generator = id_generator
855
928
self .instrumentation_info = instrumentation_info
929
+ self ._limits = None
930
+
931
+ def _with_limits (self , limits : _Limits ) -> "Tracer" :
932
+ self ._limits = limits
933
+ return self
856
934
857
935
@contextmanager
858
936
def start_as_current_span (
@@ -954,6 +1032,7 @@ def start_span( # pylint: disable=too-many-locals
954
1032
instrumentation_info = self .instrumentation_info ,
955
1033
record_exception = record_exception ,
956
1034
set_status_on_exception = set_status_on_exception ,
1035
+ limits = self ._limits ,
957
1036
)
958
1037
span .start (start_time = start_time , parent_context = context )
959
1038
else :
@@ -983,6 +1062,7 @@ def __init__(
983
1062
self .id_generator = id_generator
984
1063
self ._resource = resource
985
1064
self .sampler = sampler
1065
+ self ._limits = _Limits ._create_from_env ()
986
1066
self ._atexit_handler = None
987
1067
if shutdown_on_exit :
988
1068
self ._atexit_handler = atexit .register (self .shutdown )
@@ -1007,7 +1087,7 @@ def get_tracer(
1007
1087
InstrumentationInfo (
1008
1088
instrumenting_module_name , instrumenting_library_version
1009
1089
),
1010
- )
1090
+ ). _with_limits ( self . _limits )
1011
1091
1012
1092
def add_span_processor (self , span_processor : SpanProcessor ) -> None :
1013
1093
"""Registers a new :class:`SpanProcessor` for this `TracerProvider`.
0 commit comments