tusk-drift-python-sdk 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tusk_drift_python_sdk-0.1.0/MANIFEST.in +5 -0
- tusk_drift_python_sdk-0.1.0/PKG-INFO +83 -0
- tusk_drift_python_sdk-0.1.0/README.md +45 -0
- tusk_drift_python_sdk-0.1.0/drift/__init__.py +68 -0
- tusk_drift_python_sdk-0.1.0/drift/core/__init__.py +66 -0
- tusk_drift_python_sdk-0.1.0/drift/core/batch_processor.py +189 -0
- tusk_drift_python_sdk-0.1.0/drift/core/communication/__init__.py +52 -0
- tusk_drift_python_sdk-0.1.0/drift/core/communication/communicator.py +737 -0
- tusk_drift_python_sdk-0.1.0/drift/core/communication/types.py +369 -0
- tusk_drift_python_sdk-0.1.0/drift/core/config.py +293 -0
- tusk_drift_python_sdk-0.1.0/drift/core/data_normalization.py +220 -0
- tusk_drift_python_sdk-0.1.0/drift/core/drift_sdk.py +587 -0
- tusk_drift_python_sdk-0.1.0/drift/core/json_schema_helper.py +236 -0
- tusk_drift_python_sdk-0.1.0/drift/core/sampling.py +51 -0
- tusk_drift_python_sdk-0.1.0/drift/core/span_serialization.py +95 -0
- tusk_drift_python_sdk-0.1.0/drift/core/trace_blocking_manager.py +210 -0
- tusk_drift_python_sdk-0.1.0/drift/core/tracing/__init__.py +5 -0
- tusk_drift_python_sdk-0.1.0/drift/core/tracing/span_exporter.py +144 -0
- tusk_drift_python_sdk-0.1.0/drift/core/types.py +228 -0
- tusk_drift_python_sdk-0.1.0/drift/instrumentation/__init__.py +6 -0
- tusk_drift_python_sdk-0.1.0/drift/instrumentation/base.py +39 -0
- tusk_drift_python_sdk-0.1.0/drift/instrumentation/fastapi/__init__.py +3 -0
- tusk_drift_python_sdk-0.1.0/drift/instrumentation/fastapi/instrumentation.py +613 -0
- tusk_drift_python_sdk-0.1.0/drift/instrumentation/flask/__init__.py +3 -0
- tusk_drift_python_sdk-0.1.0/drift/instrumentation/flask/instrumentation.py +423 -0
- tusk_drift_python_sdk-0.1.0/drift/instrumentation/http/__init__.py +5 -0
- tusk_drift_python_sdk-0.1.0/drift/instrumentation/http/transform_engine.py +748 -0
- tusk_drift_python_sdk-0.1.0/drift/instrumentation/registry.py +106 -0
- tusk_drift_python_sdk-0.1.0/drift/instrumentation/requests/__init__.py +5 -0
- tusk_drift_python_sdk-0.1.0/drift/instrumentation/requests/instrumentation.py +698 -0
- tusk_drift_python_sdk-0.1.0/drift/tracing/__init__.py +1 -0
- tusk_drift_python_sdk-0.1.0/drift/tracing/adapters/__init__.py +28 -0
- tusk_drift_python_sdk-0.1.0/drift/tracing/adapters/api.py +250 -0
- tusk_drift_python_sdk-0.1.0/drift/tracing/adapters/base.py +77 -0
- tusk_drift_python_sdk-0.1.0/drift/tracing/adapters/filesystem.py +200 -0
- tusk_drift_python_sdk-0.1.0/drift/tracing/adapters/memory.py +157 -0
- tusk_drift_python_sdk-0.1.0/drift/version.py +10 -0
- tusk_drift_python_sdk-0.1.0/py.typed +0 -0
- tusk_drift_python_sdk-0.1.0/pyproject.toml +55 -0
- tusk_drift_python_sdk-0.1.0/setup.cfg +4 -0
- tusk_drift_python_sdk-0.1.0/tests/test_transform_engine.py +138 -0
- tusk_drift_python_sdk-0.1.0/tusk_drift_python_sdk.egg-info/PKG-INFO +83 -0
- tusk_drift_python_sdk-0.1.0/tusk_drift_python_sdk.egg-info/SOURCES.txt +44 -0
- tusk_drift_python_sdk-0.1.0/tusk_drift_python_sdk.egg-info/dependency_links.txt +1 -0
- tusk_drift_python_sdk-0.1.0/tusk_drift_python_sdk.egg-info/requires.txt +20 -0
- tusk_drift_python_sdk-0.1.0/tusk_drift_python_sdk.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tusk-drift-python-sdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python SDK for Tusk Drift instrumentation and replay
|
|
5
|
+
Author-email: Tusk <support@usetusk.ai>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://usetusk.ai
|
|
8
|
+
Project-URL: Documentation, https://docs.usetusk.ai
|
|
9
|
+
Project-URL: Repository, https://github.com/Use-Tusk/drift-node-sdk
|
|
10
|
+
Project-URL: Issues, https://github.com/Use-Tusk/drift-node-sdk/issues
|
|
11
|
+
Keywords: tusk,drift,testing,instrumentation,tracing,replay
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Topic :: Software Development :: Testing
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
|
+
Requires-Python: >=3.12
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
Requires-Dist: protobuf>=6.0
|
|
22
|
+
Requires-Dist: PyYAML>=6.0
|
|
23
|
+
Requires-Dist: requests>=2.32.5
|
|
24
|
+
Requires-Dist: tusk-drift-schemas>=0.1.9
|
|
25
|
+
Requires-Dist: aiohttp>=3.9.0
|
|
26
|
+
Requires-Dist: aiofiles>=23.0.0
|
|
27
|
+
Provides-Extra: flask
|
|
28
|
+
Requires-Dist: Flask>=3.1.2; extra == "flask"
|
|
29
|
+
Provides-Extra: fastapi
|
|
30
|
+
Requires-Dist: fastapi>=0.115.6; extra == "fastapi"
|
|
31
|
+
Requires-Dist: uvicorn>=0.34.2; extra == "fastapi"
|
|
32
|
+
Requires-Dist: starlette<0.42.0; extra == "fastapi"
|
|
33
|
+
Provides-Extra: dev
|
|
34
|
+
Requires-Dist: Flask>=3.1.2; extra == "dev"
|
|
35
|
+
Requires-Dist: fastapi>=0.115.6; extra == "dev"
|
|
36
|
+
Requires-Dist: uvicorn>=0.34.2; extra == "dev"
|
|
37
|
+
Requires-Dist: python-jsonpath>=0.10; extra == "dev"
|
|
38
|
+
|
|
39
|
+
# Drift Python SDK
|
|
40
|
+
|
|
41
|
+
## Installation
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
python -m venv venv
|
|
45
|
+
. venv/bin/activate
|
|
46
|
+
pip install -r requirements.txt
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Alternatively, if you have [direnv](https://direnv.net/), just allow this folder and run `pip install -r requirements.txt`.
|
|
50
|
+
|
|
51
|
+
## Running Tests
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
# Run all unit tests
|
|
55
|
+
python -m unittest discover -s tests/unit -v
|
|
56
|
+
|
|
57
|
+
# Run all integration tests (Flask/FastAPI)
|
|
58
|
+
timeout 30 python -m unittest discover -s tests/integration -v
|
|
59
|
+
|
|
60
|
+
# Run specific test file
|
|
61
|
+
python -m unittest tests.unit.test_json_schema_helper -v
|
|
62
|
+
python -m unittest tests.unit.test_adapters -v
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Database Integration Tests
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
# Start test databases
|
|
69
|
+
docker compose -f docker-compose.test.yml up -d
|
|
70
|
+
|
|
71
|
+
# Run database tests
|
|
72
|
+
python -m unittest tests.integration.test_database -v
|
|
73
|
+
|
|
74
|
+
# Stop databases
|
|
75
|
+
docker compose -f docker-compose.test.yml down
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Demo Scripts
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
timeout 10 python tests/test_flask_demo.py
|
|
82
|
+
timeout 10 python tests/test_fastapi_demo.py
|
|
83
|
+
```
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Drift Python SDK
|
|
2
|
+
|
|
3
|
+
## Installation
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
python -m venv venv
|
|
7
|
+
. venv/bin/activate
|
|
8
|
+
pip install -r requirements.txt
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Alternatively, if you have [direnv](https://direnv.net/), just allow this folder and run `pip install -r requirements.txt`.
|
|
12
|
+
|
|
13
|
+
## Running Tests
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Run all unit tests
|
|
17
|
+
python -m unittest discover -s tests/unit -v
|
|
18
|
+
|
|
19
|
+
# Run all integration tests (Flask/FastAPI)
|
|
20
|
+
timeout 30 python -m unittest discover -s tests/integration -v
|
|
21
|
+
|
|
22
|
+
# Run specific test file
|
|
23
|
+
python -m unittest tests.unit.test_json_schema_helper -v
|
|
24
|
+
python -m unittest tests.unit.test_adapters -v
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Database Integration Tests
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# Start test databases
|
|
31
|
+
docker compose -f docker-compose.test.yml up -d
|
|
32
|
+
|
|
33
|
+
# Run database tests
|
|
34
|
+
python -m unittest tests.integration.test_database -v
|
|
35
|
+
|
|
36
|
+
# Stop databases
|
|
37
|
+
docker compose -f docker-compose.test.yml down
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Demo Scripts
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
timeout 10 python tests/test_flask_demo.py
|
|
44
|
+
timeout 10 python tests/test_fastapi_demo.py
|
|
45
|
+
```
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""Drift Python SDK for distributed tracing and instrumentation."""
|
|
2
|
+
|
|
3
|
+
from .core import (
|
|
4
|
+
TuskDrift,
|
|
5
|
+
CleanSpanData,
|
|
6
|
+
PackageType,
|
|
7
|
+
SpanKind,
|
|
8
|
+
StatusCode,
|
|
9
|
+
DriftMode,
|
|
10
|
+
BatchSpanProcessorConfig,
|
|
11
|
+
# Config
|
|
12
|
+
TuskConfig,
|
|
13
|
+
TuskFileConfig,
|
|
14
|
+
ServiceConfig,
|
|
15
|
+
RecordingConfig,
|
|
16
|
+
TracesConfig,
|
|
17
|
+
TuskApiConfig,
|
|
18
|
+
load_tusk_config,
|
|
19
|
+
find_project_root,
|
|
20
|
+
)
|
|
21
|
+
from .instrumentation.flask import FlaskInstrumentation
|
|
22
|
+
from .instrumentation.fastapi import FastAPIInstrumentation
|
|
23
|
+
from .instrumentation.requests import RequestsInstrumentation
|
|
24
|
+
from .tracing.adapters import (
|
|
25
|
+
SpanExportAdapter,
|
|
26
|
+
ExportResult,
|
|
27
|
+
ExportResultCode,
|
|
28
|
+
InMemorySpanAdapter,
|
|
29
|
+
FilesystemSpanAdapter,
|
|
30
|
+
ApiSpanAdapter,
|
|
31
|
+
ApiSpanAdapterConfig,
|
|
32
|
+
create_api_adapter,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
__version__ = "0.1.0"
|
|
36
|
+
|
|
37
|
+
__all__ = [
|
|
38
|
+
# Core
|
|
39
|
+
"TuskDrift",
|
|
40
|
+
"CleanSpanData",
|
|
41
|
+
"PackageType",
|
|
42
|
+
"SpanKind",
|
|
43
|
+
"StatusCode",
|
|
44
|
+
"DriftMode",
|
|
45
|
+
"BatchSpanProcessorConfig",
|
|
46
|
+
# Config
|
|
47
|
+
"TuskConfig",
|
|
48
|
+
"TuskFileConfig",
|
|
49
|
+
"ServiceConfig",
|
|
50
|
+
"RecordingConfig",
|
|
51
|
+
"TracesConfig",
|
|
52
|
+
"TuskApiConfig",
|
|
53
|
+
"load_tusk_config",
|
|
54
|
+
"find_project_root",
|
|
55
|
+
# Instrumentations
|
|
56
|
+
"FlaskInstrumentation",
|
|
57
|
+
"FastAPIInstrumentation",
|
|
58
|
+
"RequestsInstrumentation",
|
|
59
|
+
# Adapters
|
|
60
|
+
"SpanExportAdapter",
|
|
61
|
+
"ExportResult",
|
|
62
|
+
"ExportResultCode",
|
|
63
|
+
"InMemorySpanAdapter",
|
|
64
|
+
"FilesystemSpanAdapter",
|
|
65
|
+
"ApiSpanAdapter",
|
|
66
|
+
"ApiSpanAdapterConfig",
|
|
67
|
+
"create_api_adapter",
|
|
68
|
+
]
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""Core module for the Drift SDK."""
|
|
2
|
+
|
|
3
|
+
from .drift_sdk import TuskDrift
|
|
4
|
+
from .types import DriftMode, CleanSpanData, PackageType, SpanKind, StatusCode
|
|
5
|
+
from .config import (
|
|
6
|
+
TuskConfig,
|
|
7
|
+
TuskFileConfig,
|
|
8
|
+
ServiceConfig,
|
|
9
|
+
RecordingConfig,
|
|
10
|
+
TracesConfig,
|
|
11
|
+
TuskApiConfig,
|
|
12
|
+
load_tusk_config,
|
|
13
|
+
find_project_root,
|
|
14
|
+
)
|
|
15
|
+
from .batch_processor import BatchSpanProcessor, BatchSpanProcessorConfig
|
|
16
|
+
from .sampling import should_sample, validate_sampling_rate
|
|
17
|
+
from .data_normalization import (
|
|
18
|
+
normalize_input_data,
|
|
19
|
+
remove_none_values,
|
|
20
|
+
create_span_input_value,
|
|
21
|
+
create_mock_input_value,
|
|
22
|
+
)
|
|
23
|
+
from .trace_blocking_manager import (
|
|
24
|
+
TraceBlockingManager,
|
|
25
|
+
estimate_span_size,
|
|
26
|
+
should_block_span,
|
|
27
|
+
MAX_SPAN_SIZE_MB,
|
|
28
|
+
MAX_SPAN_SIZE_BYTES,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
__all__ = [
|
|
32
|
+
# Main SDK
|
|
33
|
+
"TuskDrift",
|
|
34
|
+
# Config
|
|
35
|
+
"TuskConfig",
|
|
36
|
+
"TuskFileConfig",
|
|
37
|
+
"ServiceConfig",
|
|
38
|
+
"RecordingConfig",
|
|
39
|
+
"TracesConfig",
|
|
40
|
+
"TuskApiConfig",
|
|
41
|
+
"load_tusk_config",
|
|
42
|
+
"find_project_root",
|
|
43
|
+
# Types
|
|
44
|
+
"DriftMode",
|
|
45
|
+
"CleanSpanData",
|
|
46
|
+
"PackageType",
|
|
47
|
+
"SpanKind",
|
|
48
|
+
"StatusCode",
|
|
49
|
+
# Batching
|
|
50
|
+
"BatchSpanProcessor",
|
|
51
|
+
"BatchSpanProcessorConfig",
|
|
52
|
+
# Sampling
|
|
53
|
+
"should_sample",
|
|
54
|
+
"validate_sampling_rate",
|
|
55
|
+
# Data normalization
|
|
56
|
+
"normalize_input_data",
|
|
57
|
+
"remove_none_values",
|
|
58
|
+
"create_span_input_value",
|
|
59
|
+
"create_mock_input_value",
|
|
60
|
+
# Trace blocking
|
|
61
|
+
"TraceBlockingManager",
|
|
62
|
+
"estimate_span_size",
|
|
63
|
+
"should_block_span",
|
|
64
|
+
"MAX_SPAN_SIZE_MB",
|
|
65
|
+
"MAX_SPAN_SIZE_BYTES",
|
|
66
|
+
]
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"""Batch span processor for efficient span export."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import logging
|
|
7
|
+
import threading
|
|
8
|
+
import time
|
|
9
|
+
from collections import deque
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from typing import TYPE_CHECKING
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from .types import CleanSpanData
|
|
15
|
+
from ..tracing.adapters.base import SpanExportAdapter
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class BatchSpanProcessorConfig:
|
|
22
|
+
"""Configuration for the batch span processor."""
|
|
23
|
+
|
|
24
|
+
# Maximum queue size before spans are dropped
|
|
25
|
+
max_queue_size: int = 2048
|
|
26
|
+
# Maximum batch size per export
|
|
27
|
+
max_export_batch_size: int = 512
|
|
28
|
+
# Interval between scheduled exports (in seconds)
|
|
29
|
+
scheduled_delay_seconds: float = 2.0
|
|
30
|
+
# Maximum time to wait for export (in seconds)
|
|
31
|
+
export_timeout_seconds: float = 30.0
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class BatchSpanProcessor:
|
|
35
|
+
"""
|
|
36
|
+
Batches spans and exports them periodically or when batch size is reached.
|
|
37
|
+
|
|
38
|
+
Matches the behavior of OpenTelemetry's BatchSpanProcessor:
|
|
39
|
+
- Queues spans in memory
|
|
40
|
+
- Exports in batches at regular intervals or when max batch size is reached
|
|
41
|
+
- Drops spans if queue is full
|
|
42
|
+
- Handles graceful shutdown with final export
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
def __init__(
|
|
46
|
+
self,
|
|
47
|
+
adapters: list["SpanExportAdapter"],
|
|
48
|
+
config: BatchSpanProcessorConfig | None = None,
|
|
49
|
+
) -> None:
|
|
50
|
+
"""
|
|
51
|
+
Initialize the batch processor.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
adapters: List of adapters to export spans to
|
|
55
|
+
config: Optional configuration (uses defaults if not provided)
|
|
56
|
+
"""
|
|
57
|
+
self._adapters = adapters
|
|
58
|
+
self._config = config or BatchSpanProcessorConfig()
|
|
59
|
+
self._queue: deque[CleanSpanData] = deque(maxlen=self._config.max_queue_size)
|
|
60
|
+
self._lock = threading.Lock()
|
|
61
|
+
self._shutdown_event = threading.Event()
|
|
62
|
+
self._export_thread: threading.Thread | None = None
|
|
63
|
+
self._started = False
|
|
64
|
+
self._dropped_spans = 0
|
|
65
|
+
|
|
66
|
+
def start(self) -> None:
|
|
67
|
+
"""Start the background export thread."""
|
|
68
|
+
if self._started:
|
|
69
|
+
return
|
|
70
|
+
|
|
71
|
+
self._started = True
|
|
72
|
+
self._shutdown_event.clear()
|
|
73
|
+
self._export_thread = threading.Thread(
|
|
74
|
+
target=self._export_loop,
|
|
75
|
+
daemon=True,
|
|
76
|
+
name="drift-batch-exporter",
|
|
77
|
+
)
|
|
78
|
+
self._export_thread.start()
|
|
79
|
+
logger.debug("BatchSpanProcessor started")
|
|
80
|
+
|
|
81
|
+
def stop(self, timeout: float | None = None) -> None:
|
|
82
|
+
"""
|
|
83
|
+
Stop the processor and export remaining spans.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
timeout: Maximum time to wait for final export
|
|
87
|
+
"""
|
|
88
|
+
if not self._started:
|
|
89
|
+
return
|
|
90
|
+
|
|
91
|
+
self._shutdown_event.set()
|
|
92
|
+
|
|
93
|
+
if self._export_thread is not None:
|
|
94
|
+
self._export_thread.join(timeout=timeout or self._config.export_timeout_seconds)
|
|
95
|
+
|
|
96
|
+
# Final export of remaining spans
|
|
97
|
+
self._force_flush()
|
|
98
|
+
|
|
99
|
+
self._started = False
|
|
100
|
+
logger.debug(f"BatchSpanProcessor stopped. Dropped {self._dropped_spans} spans total.")
|
|
101
|
+
|
|
102
|
+
def add_span(self, span: "CleanSpanData") -> bool:
|
|
103
|
+
"""
|
|
104
|
+
Add a span to the queue for export.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
span: The span to add
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
True if span was added, False if queue is full and span was dropped
|
|
111
|
+
"""
|
|
112
|
+
with self._lock:
|
|
113
|
+
if len(self._queue) >= self._config.max_queue_size:
|
|
114
|
+
self._dropped_spans += 1
|
|
115
|
+
logger.warning(
|
|
116
|
+
f"Span queue full ({self._config.max_queue_size}), dropping span. "
|
|
117
|
+
f"Total dropped: {self._dropped_spans}"
|
|
118
|
+
)
|
|
119
|
+
return False
|
|
120
|
+
|
|
121
|
+
self._queue.append(span)
|
|
122
|
+
|
|
123
|
+
# Trigger immediate export if batch size reached
|
|
124
|
+
if len(self._queue) >= self._config.max_export_batch_size:
|
|
125
|
+
# Signal export thread to wake up (if using condition variable)
|
|
126
|
+
pass
|
|
127
|
+
|
|
128
|
+
return True
|
|
129
|
+
|
|
130
|
+
def _export_loop(self) -> None:
|
|
131
|
+
"""Background thread that periodically exports spans."""
|
|
132
|
+
while not self._shutdown_event.is_set():
|
|
133
|
+
# Wait for scheduled delay or shutdown
|
|
134
|
+
self._shutdown_event.wait(timeout=self._config.scheduled_delay_seconds)
|
|
135
|
+
|
|
136
|
+
if self._shutdown_event.is_set():
|
|
137
|
+
break
|
|
138
|
+
|
|
139
|
+
self._export_batch()
|
|
140
|
+
|
|
141
|
+
def _export_batch(self) -> None:
|
|
142
|
+
"""Export a batch of spans from the queue."""
|
|
143
|
+
# Get batch of spans
|
|
144
|
+
batch: list[CleanSpanData] = []
|
|
145
|
+
with self._lock:
|
|
146
|
+
while self._queue and len(batch) < self._config.max_export_batch_size:
|
|
147
|
+
batch.append(self._queue.popleft())
|
|
148
|
+
|
|
149
|
+
if not batch:
|
|
150
|
+
return
|
|
151
|
+
|
|
152
|
+
# Export to all adapters
|
|
153
|
+
for adapter in self._adapters:
|
|
154
|
+
try:
|
|
155
|
+
# Handle async adapters
|
|
156
|
+
if asyncio.iscoroutinefunction(adapter.export_spans):
|
|
157
|
+
try:
|
|
158
|
+
loop = asyncio.get_running_loop()
|
|
159
|
+
asyncio.create_task(adapter.export_spans(batch))
|
|
160
|
+
except RuntimeError:
|
|
161
|
+
# No running loop, create one
|
|
162
|
+
asyncio.run(adapter.export_spans(batch))
|
|
163
|
+
else:
|
|
164
|
+
adapter.export_spans(batch) # type: ignore
|
|
165
|
+
|
|
166
|
+
logger.debug(f"Exported {len(batch)} spans via {adapter.name}")
|
|
167
|
+
|
|
168
|
+
except Exception as e:
|
|
169
|
+
logger.error(f"Failed to export batch via {adapter.name}: {e}")
|
|
170
|
+
|
|
171
|
+
def _force_flush(self) -> None:
|
|
172
|
+
"""Force export all remaining spans in the queue."""
|
|
173
|
+
while True:
|
|
174
|
+
with self._lock:
|
|
175
|
+
if not self._queue:
|
|
176
|
+
break
|
|
177
|
+
|
|
178
|
+
self._export_batch()
|
|
179
|
+
|
|
180
|
+
@property
|
|
181
|
+
def queue_size(self) -> int:
|
|
182
|
+
"""Get the current queue size."""
|
|
183
|
+
with self._lock:
|
|
184
|
+
return len(self._queue)
|
|
185
|
+
|
|
186
|
+
@property
|
|
187
|
+
def dropped_span_count(self) -> int:
|
|
188
|
+
"""Get the number of dropped spans."""
|
|
189
|
+
return self._dropped_spans
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""CLI communication module for Drift SDK.
|
|
2
|
+
|
|
3
|
+
This module handles bidirectional communication between the SDK and the Tusk CLI
|
|
4
|
+
for replay testing. Communication uses Protocol Buffers over Unix sockets or TCP.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .types import (
|
|
8
|
+
MessageType,
|
|
9
|
+
SDKMessageType,
|
|
10
|
+
CLIMessageType,
|
|
11
|
+
ConnectRequest,
|
|
12
|
+
ConnectResponse,
|
|
13
|
+
GetMockRequest,
|
|
14
|
+
GetMockResponse,
|
|
15
|
+
EnvVarRequest,
|
|
16
|
+
EnvVarResponse,
|
|
17
|
+
MockRequestInput,
|
|
18
|
+
MockResponseOutput,
|
|
19
|
+
# Protobuf types (re-exported)
|
|
20
|
+
SdkMessage,
|
|
21
|
+
CliMessage,
|
|
22
|
+
span_to_proto,
|
|
23
|
+
dict_to_span,
|
|
24
|
+
extract_response_data,
|
|
25
|
+
)
|
|
26
|
+
from .communicator import ProtobufCommunicator, CommunicatorConfig
|
|
27
|
+
|
|
28
|
+
__all__ = [
|
|
29
|
+
# Message types
|
|
30
|
+
"MessageType",
|
|
31
|
+
"SDKMessageType",
|
|
32
|
+
"CLIMessageType",
|
|
33
|
+
# Request/Response types
|
|
34
|
+
"ConnectRequest",
|
|
35
|
+
"ConnectResponse",
|
|
36
|
+
"GetMockRequest",
|
|
37
|
+
"GetMockResponse",
|
|
38
|
+
"EnvVarRequest",
|
|
39
|
+
"EnvVarResponse",
|
|
40
|
+
"MockRequestInput",
|
|
41
|
+
"MockResponseOutput",
|
|
42
|
+
# Protobuf types
|
|
43
|
+
"SdkMessage",
|
|
44
|
+
"CliMessage",
|
|
45
|
+
# Utilities
|
|
46
|
+
"span_to_proto",
|
|
47
|
+
"dict_to_span",
|
|
48
|
+
"extract_response_data",
|
|
49
|
+
# Communicator
|
|
50
|
+
"ProtobufCommunicator",
|
|
51
|
+
"CommunicatorConfig",
|
|
52
|
+
]
|