Skip to content

Commit c05ebc1

Browse files
committed
bugfix #3921
1 parent d73593d commit c05ebc1

File tree

3 files changed

+38
-2
lines changed

3 files changed

+38
-2
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
5454
- unit annotations (enclosed in curly braces like `{requests}`) are stripped away
5555
- units with slash are converted e.g. `m/s` -> `meters_per_second`.
5656
- The exporter's API is not changed
57+
- Fix RandomIdGenerator can generate invalid Span/Trace Ids
58+
([#3921](https://github.com/open-telemetry/opentelemetry-python/issues/3921))
5759

5860
## Version 1.24.0/0.45b0 (2024-03-28)
5961

opentelemetry-sdk/src/opentelemetry/sdk/trace/id_generator.py

+10-2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
import abc
1616
import random
1717

18+
from opentelemetry.trace.span import INVALID_SPAN_ID, INVALID_TRACE_ID
19+
1820

1921
class IdGenerator(abc.ABC):
2022
@abc.abstractmethod
@@ -46,7 +48,13 @@ class RandomIdGenerator(IdGenerator):
4648
"""
4749

4850
def generate_span_id(self) -> int:
49-
return random.getrandbits(64)
51+
span_id = random.getrandbits(64)
52+
while span_id == INVALID_SPAN_ID:
53+
span_id = random.getrandbits(64)
54+
return span_id
5055

5156
def generate_trace_id(self) -> int:
52-
return random.getrandbits(128)
57+
trace_id = random.getrandbits(128)
58+
while trace_id == INVALID_TRACE_ID:
59+
trace_id = random.getrandbits(128)
60+
return trace_id

opentelemetry-sdk/tests/trace/test_trace.py

+26
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
StatusCode,
6363
get_tracer,
6464
set_tracer_provider,
65+
span,
6566
)
6667

6768

@@ -2061,3 +2062,28 @@ def test_tracer_provider_init_default(self, resource_patch, sample_patch):
20612062
sample_patch.assert_called_once()
20622063
self.assertIsNotNone(tracer_provider._span_limits)
20632064
self.assertIsNotNone(tracer_provider._atexit_handler)
2065+
2066+
2067+
class TestRandomIdGenerator(unittest.TestCase):
2068+
_TRACE_ID_MAX_VALUE = 2 ** 128 - 1
2069+
_SPAN_ID_MAX_VALUE = 2 ** 64 - 1
2070+
2071+
@patch('random.getrandbits', side_effect=[span.INVALID_SPAN_ID, 0x00000000DEADBEF0])
2072+
def test_generate_span_id_avoids_invalid(self, mock_getrandbits):
2073+
generator = RandomIdGenerator()
2074+
span_id = generator.generate_span_id()
2075+
2076+
self.assertNotEqual(span_id, span.INVALID_SPAN_ID)
2077+
self.assertGreater(span_id, span.INVALID_SPAN_ID)
2078+
self.assertLessEqual(span_id, self._SPAN_ID_MAX_VALUE)
2079+
self.assertEqual(mock_getrandbits.call_count, 2) # Ensure exactly two calls
2080+
2081+
@patch('random.getrandbits', side_effect=[span.INVALID_TRACE_ID, 0x000000000000000000000000DEADBEEF])
2082+
def test_generate_trace_id_avoids_invalid(self, mock_getrandbits):
2083+
generator = RandomIdGenerator()
2084+
trace_id = generator.generate_trace_id()
2085+
2086+
self.assertNotEqual(trace_id, span.INVALID_TRACE_ID)
2087+
self.assertGreater(trace_id, span.INVALID_TRACE_ID)
2088+
self.assertLessEqual(trace_id, self._TRACE_ID_MAX_VALUE)
2089+
self.assertEqual(mock_getrandbits.call_count, 2) # Ensure exactly two calls

0 commit comments

Comments
 (0)