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