22import uuid
33import contextlib
44import time
5+ import itertools
6+ import platform
57
68from datetime import datetime , timedelta
79
2527 from typing import Dict
2628 from typing import List
2729 from typing import Tuple
30+ from typing import Iterator
2831
2932_traceparent_header_format_re = re .compile (
3033 "^[ \t ]*" # whitespace
3437 "[ \t ]*$" # whitespace
3538)
3639
40+ _ITERTOOLS_COUNT_IS_ATOMIC = platform .python_implementation () in ("CPython" , "PyPy" )
41+
3742
3843class EnvironHeaders (Mapping ): # type: ignore
3944 def __init__ (
@@ -107,6 +112,7 @@ class Span(object):
107112 "_span_recorder" ,
108113 "hub" ,
109114 "_context_manager_state" ,
115+ "_span_id_generator" ,
110116 )
111117
112118 def __new__ (cls , ** kwargs ):
@@ -130,10 +136,20 @@ def __init__(
130136 hub = None , # type: Optional[sentry_sdk.Hub]
131137 status = None , # type: Optional[str]
132138 transaction = None , # type: Optional[str] # deprecated
139+ _span_id_generator = None , # type: Optional[Iterator[str]]
133140 ):
134141 # type: (...) -> None
135142 self .trace_id = trace_id or uuid .uuid4 ().hex
136- self .span_id = span_id or uuid .uuid4 ().hex [16 :]
143+
144+ if not span_id and _span_id_generator :
145+ span_id = next (_span_id_generator )
146+
147+ if not span_id :
148+ span_id = uuid .uuid4 ().hex [16 :]
149+
150+ self .span_id = span_id
151+ self ._span_id_generator = _span_id_generator
152+
137153 self .parent_span_id = parent_span_id
138154 self .same_process_as_parent = same_process_as_parent
139155 self .sampled = sampled
@@ -206,7 +222,9 @@ def start_child(self, **kwargs):
206222 kwargs .setdefault ("sampled" , self .sampled )
207223
208224 rv = Span (
209- trace_id = self .trace_id , span_id = None , parent_span_id = self .span_id , ** kwargs
225+ trace_id = self .trace_id , span_id = None , parent_span_id = self .span_id ,
226+ _span_id_generator = self ._span_id_generator ,
227+ ** kwargs
210228 )
211229
212230 rv ._span_recorder = recorder = self ._span_recorder
@@ -435,6 +453,15 @@ def __init__(
435453 "instead of Span(transaction=...)."
436454 )
437455 name = kwargs .pop ("transaction" )
456+
457+ if "_span_id_generator" in kwargs :
458+ raise TypeError ("Argument _span_id_generator can only be provided to Spans" )
459+
460+ # next() on itertools.count() is a way to get-and-increment an integer
461+ # "atomically" on Python runtimes with a GIL
462+ if _ITERTOOLS_COUNT_IS_ATOMIC and kwargs .pop ("_fast_span_ids" , None ):
463+ kwargs ['_span_id_generator' ] = ("{:016x}" .format (i ) for i in itertools .count ())
464+
438465 Span .__init__ (self , ** kwargs )
439466 self .name = name
440467
0 commit comments