56
56
from opentelemetry .sdk .util import BoundedDict , BoundedList
57
57
from opentelemetry .sdk .util .instrumentation import InstrumentationInfo
58
58
from opentelemetry .trace import SpanContext
59
- from opentelemetry .trace .propagation import SPAN_KEY
60
59
from opentelemetry .trace .status import Status , StatusCode
61
60
from opentelemetry .util import types
62
61
from opentelemetry .util ._time import _time_ns
63
62
64
63
logger = logging .getLogger (__name__ )
65
64
66
- SPAN_ATTRIBUTE_COUNT_LIMIT = int (
67
- environ . get ( OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT , 128 )
68
- )
65
+ _DEFAULT_SPAN_EVENTS_LIMIT = 128
66
+ _DEFAULT_SPAN_LINKS_LIMIT = 128
67
+ _DEFAULT_SPAN_ATTRIBUTES_LIMIT = 128
69
68
70
- _SPAN_EVENT_COUNT_LIMIT = int (environ .get (OTEL_SPAN_EVENT_COUNT_LIMIT , 128 ))
71
- _SPAN_LINK_COUNT_LIMIT = int (environ .get (OTEL_SPAN_LINK_COUNT_LIMIT , 128 ))
72
69
# pylint: disable=protected-access
73
70
_TRACE_SAMPLER = sampling ._get_from_env_or_default ()
74
71
@@ -502,6 +499,87 @@ def _format_links(links):
502
499
return f_links
503
500
504
501
502
+ class _Limits :
503
+ """The limits that should be enforce on recorded data such as events, links, attributes etc.
504
+
505
+ This class does not enforce any limits itself. It only provides an a way read limits from env,
506
+ default values and in future from user provided arguments.
507
+
508
+ All limit must be either a non-negative integer or ``None``.
509
+ Setting a limit to ``None`` will not set any limits for that field/type.
510
+
511
+ Args:
512
+ max_events: Maximum number of events that can be added to a Span.
513
+ max_links: Maximum number of links that can be added to a Span.
514
+ max_attributes: Maximum number of attributes that can be added to a Span.
515
+ """
516
+
517
+ UNSET = - 1
518
+
519
+ max_attributes : int
520
+ max_events : int
521
+ max_links : int
522
+
523
+ def __init__ (
524
+ self ,
525
+ max_attributes : Optional [int ] = None ,
526
+ max_events : Optional [int ] = None ,
527
+ max_links : Optional [int ] = None ,
528
+ ):
529
+ self .max_attributes = self ._from_env_if_absent (
530
+ max_attributes ,
531
+ OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT ,
532
+ _DEFAULT_SPAN_ATTRIBUTES_LIMIT ,
533
+ )
534
+ self .max_events = self ._from_env_if_absent (
535
+ max_events , OTEL_SPAN_EVENT_COUNT_LIMIT , _DEFAULT_SPAN_EVENTS_LIMIT
536
+ )
537
+ self .max_links = self ._from_env_if_absent (
538
+ max_links , OTEL_SPAN_LINK_COUNT_LIMIT , _DEFAULT_SPAN_LINKS_LIMIT
539
+ )
540
+
541
+ def __repr__ (self ):
542
+ return "max_attributes={}, max_events={}, max_links={}" .format (
543
+ self .max_attributes , self .max_events , self .max_links
544
+ )
545
+
546
+ @classmethod
547
+ def _from_env_if_absent (
548
+ cls , value : Optional [int ], env_var : str , default : Optional [int ]
549
+ ) -> Optional [int ]:
550
+ if value is cls .UNSET :
551
+ return None
552
+
553
+ err_msg = "{0} must be a non-negative integer but got {}"
554
+
555
+ if value is None :
556
+ str_value = environ .get (env_var , "" ).strip ().lower ()
557
+ if not str_value :
558
+ return default
559
+ if str_value == "unset" :
560
+ return None
561
+
562
+ try :
563
+ value = int (str_value )
564
+ except ValueError :
565
+ raise ValueError (err_msg .format (env_var , str_value ))
566
+
567
+ if value < 0 :
568
+ raise ValueError (err_msg .format (env_var , value ))
569
+ return value
570
+
571
+
572
+ _UnsetLimits = _Limits (
573
+ max_attributes = _Limits .UNSET ,
574
+ max_events = _Limits .UNSET ,
575
+ max_links = _Limits .UNSET ,
576
+ )
577
+
578
+ SPAN_ATTRIBUTE_COUNT_LIMIT = _Limits ._from_env_if_absent (
579
+ None , OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT , _DEFAULT_SPAN_ATTRIBUTES_LIMIT
580
+ )
581
+
582
+
505
583
class Span (trace_api .Span , ReadableSpan ):
506
584
"""See `opentelemetry.trace.Span`.
507
585
@@ -566,7 +644,7 @@ def __init__(
566
644
self ._attributes = self ._new_attributes ()
567
645
else :
568
646
self ._attributes = BoundedDict .from_map (
569
- SPAN_ATTRIBUTE_COUNT_LIMIT , attributes
647
+ self . _limits . max_attributes , attributes
570
648
)
571
649
572
650
self ._events = self ._new_events ()
@@ -582,24 +660,21 @@ def __init__(
582
660
if links is None :
583
661
self ._links = self ._new_links ()
584
662
else :
585
- self ._links = BoundedList .from_seq (_SPAN_LINK_COUNT_LIMIT , links )
663
+ self ._links = BoundedList .from_seq (self . _limits . max_links , links )
586
664
587
665
def __repr__ (self ):
588
666
return '{}(name="{}", context={})' .format (
589
667
type (self ).__name__ , self ._name , self ._context
590
668
)
591
669
592
- @staticmethod
593
- def _new_attributes ():
594
- return BoundedDict (SPAN_ATTRIBUTE_COUNT_LIMIT )
670
+ def _new_attributes (self ):
671
+ return BoundedDict (self ._limits .max_attributes )
595
672
596
- @staticmethod
597
- def _new_events ():
598
- return BoundedList (_SPAN_EVENT_COUNT_LIMIT )
673
+ def _new_events (self ):
674
+ return BoundedList (self ._limits .max_events )
599
675
600
- @staticmethod
601
- def _new_links ():
602
- return BoundedList (_SPAN_LINK_COUNT_LIMIT )
676
+ def _new_links (self ):
677
+ return BoundedList (self ._limits .max_links )
603
678
604
679
def get_span_context (self ):
605
680
return self ._context
@@ -772,6 +847,10 @@ class _Span(Span):
772
847
by other mechanisms than through the `Tracer`.
773
848
"""
774
849
850
+ def __init__ (self , * args , limits = _UnsetLimits , ** kwargs ):
851
+ self ._limits = limits
852
+ super ().__init__ (* args , ** kwargs )
853
+
775
854
776
855
class Tracer (trace_api .Tracer ):
777
856
"""See `opentelemetry.trace.Tracer`."""
@@ -791,6 +870,7 @@ def __init__(
791
870
self .span_processor = span_processor
792
871
self .id_generator = id_generator
793
872
self .instrumentation_info = instrumentation_info
873
+ self ._limits = None
794
874
795
875
@contextmanager
796
876
def start_as_current_span (
@@ -892,6 +972,7 @@ def start_span( # pylint: disable=too-many-locals
892
972
instrumentation_info = self .instrumentation_info ,
893
973
record_exception = record_exception ,
894
974
set_status_on_exception = set_status_on_exception ,
975
+ limits = self ._limits ,
895
976
)
896
977
span .start (start_time = start_time , parent_context = context )
897
978
else :
@@ -921,6 +1002,7 @@ def __init__(
921
1002
self .id_generator = id_generator
922
1003
self ._resource = resource
923
1004
self .sampler = sampler
1005
+ self ._limits = _Limits ()
924
1006
self ._atexit_handler = None
925
1007
if shutdown_on_exit :
926
1008
self ._atexit_handler = atexit .register (self .shutdown )
@@ -937,7 +1019,7 @@ def get_tracer(
937
1019
if not instrumenting_module_name : # Reject empty strings too.
938
1020
instrumenting_module_name = ""
939
1021
logger .error ("get_tracer called with missing module name." )
940
- return Tracer (
1022
+ tracer = Tracer (
941
1023
self .sampler ,
942
1024
self .resource ,
943
1025
self ._active_span_processor ,
@@ -946,6 +1028,8 @@ def get_tracer(
946
1028
instrumenting_module_name , instrumenting_library_version
947
1029
),
948
1030
)
1031
+ tracer ._limits = self ._limits
1032
+ return tracer
949
1033
950
1034
def add_span_processor (self , span_processor : SpanProcessor ) -> None :
951
1035
"""Registers a new :class:`SpanProcessor` for this `TracerProvider`.
0 commit comments