|
4 | 4 | import json |
5 | 5 | import logging |
6 | 6 | import os |
| 7 | +import random |
7 | 8 | import sys |
8 | 9 | from contextlib import contextmanager |
| 10 | +from dataclasses import dataclass |
9 | 11 | from pathlib import Path |
10 | 12 | from types import TracebackType |
11 | 13 | from typing import TYPE_CHECKING, Any, Dict, List, Mapping, Sequence, Tuple, TypedDict, TypeVar, Union |
12 | 14 |
|
13 | 15 | from opentelemetry import context, trace as trace_api |
14 | 16 | from opentelemetry.sdk.resources import Resource |
15 | 17 | from opentelemetry.sdk.trace import Event, ReadableSpan |
| 18 | +from opentelemetry.sdk.trace.id_generator import IdGenerator |
16 | 19 | from opentelemetry.sdk.util.instrumentation import InstrumentationScope |
17 | 20 | from opentelemetry.trace.status import Status |
18 | 21 | from opentelemetry.util import types as otel_types |
@@ -348,3 +351,33 @@ def maybe_capture_server_headers(capture: bool): |
348 | 351 |
|
349 | 352 | def is_asgi_send_receive_span_name(name: str) -> bool: |
350 | 353 | return name.endswith((' http send', ' http receive', ' websocket send', ' websocket receive')) |
| 354 | + |
| 355 | + |
| 356 | +@dataclass(repr=True) |
| 357 | +class SeededRandomIdGenerator(IdGenerator): |
| 358 | + """Generate random span/trace IDs from a seed for deterministic tests. |
| 359 | +
|
| 360 | + Similar to RandomIdGenerator from OpenTelemetry, but with a seed. |
| 361 | + Set the seed to None for non-deterministic randomness. |
| 362 | + In that case the difference from RandomIdGenerator is that it's not affected by `random.seed(...)`. |
| 363 | +
|
| 364 | + Trace IDs are 128-bit integers. |
| 365 | + Span IDs are 64-bit integers. |
| 366 | + """ |
| 367 | + |
| 368 | + seed: int | None = 0 |
| 369 | + |
| 370 | + def __post_init__(self) -> None: |
| 371 | + self.random = random.Random(self.seed) |
| 372 | + |
| 373 | + def generate_span_id(self) -> int: |
| 374 | + span_id = self.random.getrandbits(64) |
| 375 | + while span_id == trace_api.INVALID_SPAN_ID: # pragma: no cover |
| 376 | + span_id = self.random.getrandbits(64) |
| 377 | + return span_id |
| 378 | + |
| 379 | + def generate_trace_id(self) -> int: |
| 380 | + trace_id = self.random.getrandbits(128) |
| 381 | + while trace_id == trace_api.INVALID_TRACE_ID: # pragma: no cover |
| 382 | + trace_id = self.random.getrandbits(128) |
| 383 | + return trace_id |
0 commit comments