microsoft-agents-a365-observability-core 0.2.1.dev36__tar.gz → 0.2.1.dev43__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.
- {microsoft_agents_a365_observability_core-0.2.1.dev36 → microsoft_agents_a365_observability_core-0.2.1.dev43}/PKG-INFO +1 -1
- {microsoft_agents_a365_observability_core-0.2.1.dev36 → microsoft_agents_a365_observability_core-0.2.1.dev43}/microsoft_agents_a365/observability/core/__init__.py +4 -0
- {microsoft_agents_a365_observability_core-0.2.1.dev36 → microsoft_agents_a365_observability_core-0.2.1.dev43}/microsoft_agents_a365/observability/core/execute_tool_scope.py +11 -8
- {microsoft_agents_a365_observability_core-0.2.1.dev36 → microsoft_agents_a365_observability_core-0.2.1.dev43}/microsoft_agents_a365/observability/core/exporters/agent365_exporter.py +26 -13
- {microsoft_agents_a365_observability_core-0.2.1.dev36 → microsoft_agents_a365_observability_core-0.2.1.dev43}/microsoft_agents_a365/observability/core/exporters/utils.py +34 -0
- {microsoft_agents_a365_observability_core-0.2.1.dev36 → microsoft_agents_a365_observability_core-0.2.1.dev43}/microsoft_agents_a365/observability/core/inference_scope.py +12 -8
- {microsoft_agents_a365_observability_core-0.2.1.dev36 → microsoft_agents_a365_observability_core-0.2.1.dev43}/microsoft_agents_a365/observability/core/invoke_agent_scope.py +11 -0
- {microsoft_agents_a365_observability_core-0.2.1.dev36 → microsoft_agents_a365_observability_core-0.2.1.dev43}/microsoft_agents_a365/observability/core/opentelemetry_scope.py +52 -22
- {microsoft_agents_a365_observability_core-0.2.1.dev36 → microsoft_agents_a365_observability_core-0.2.1.dev43}/microsoft_agents_a365/observability/core/spans_scopes/output_scope.py +19 -8
- {microsoft_agents_a365_observability_core-0.2.1.dev36 → microsoft_agents_a365_observability_core-0.2.1.dev43}/microsoft_agents_a365/observability/core/utils.py +35 -102
- {microsoft_agents_a365_observability_core-0.2.1.dev36 → microsoft_agents_a365_observability_core-0.2.1.dev43}/microsoft_agents_a365_observability_core.egg-info/PKG-INFO +1 -1
- {microsoft_agents_a365_observability_core-0.2.1.dev36 → microsoft_agents_a365_observability_core-0.2.1.dev43}/README.md +0 -0
- {microsoft_agents_a365_observability_core-0.2.1.dev36 → microsoft_agents_a365_observability_core-0.2.1.dev43}/microsoft_agents_a365/observability/core/agent_details.py +0 -0
- {microsoft_agents_a365_observability_core-0.2.1.dev36 → microsoft_agents_a365_observability_core-0.2.1.dev43}/microsoft_agents_a365/observability/core/config.py +0 -0
- {microsoft_agents_a365_observability_core-0.2.1.dev36 → microsoft_agents_a365_observability_core-0.2.1.dev43}/microsoft_agents_a365/observability/core/constants.py +0 -0
- {microsoft_agents_a365_observability_core-0.2.1.dev36 → microsoft_agents_a365_observability_core-0.2.1.dev43}/microsoft_agents_a365/observability/core/execution_type.py +0 -0
- {microsoft_agents_a365_observability_core-0.2.1.dev36 → microsoft_agents_a365_observability_core-0.2.1.dev43}/microsoft_agents_a365/observability/core/exporters/__init__.py +0 -0
- {microsoft_agents_a365_observability_core-0.2.1.dev36 → microsoft_agents_a365_observability_core-0.2.1.dev43}/microsoft_agents_a365/observability/core/exporters/agent365_exporter_options.py +0 -0
- {microsoft_agents_a365_observability_core-0.2.1.dev36 → microsoft_agents_a365_observability_core-0.2.1.dev43}/microsoft_agents_a365/observability/core/exporters/enriched_span.py +0 -0
- {microsoft_agents_a365_observability_core-0.2.1.dev36 → microsoft_agents_a365_observability_core-0.2.1.dev43}/microsoft_agents_a365/observability/core/exporters/enriching_span_processor.py +0 -0
- {microsoft_agents_a365_observability_core-0.2.1.dev36 → microsoft_agents_a365_observability_core-0.2.1.dev43}/microsoft_agents_a365/observability/core/exporters/spectra_exporter_options.py +0 -0
- {microsoft_agents_a365_observability_core-0.2.1.dev36 → microsoft_agents_a365_observability_core-0.2.1.dev43}/microsoft_agents_a365/observability/core/inference_call_details.py +0 -0
- {microsoft_agents_a365_observability_core-0.2.1.dev36 → microsoft_agents_a365_observability_core-0.2.1.dev43}/microsoft_agents_a365/observability/core/inference_operation_type.py +0 -0
- {microsoft_agents_a365_observability_core-0.2.1.dev36 → microsoft_agents_a365_observability_core-0.2.1.dev43}/microsoft_agents_a365/observability/core/invoke_agent_details.py +0 -0
- {microsoft_agents_a365_observability_core-0.2.1.dev36 → microsoft_agents_a365_observability_core-0.2.1.dev43}/microsoft_agents_a365/observability/core/middleware/__init__.py +0 -0
- {microsoft_agents_a365_observability_core-0.2.1.dev36 → microsoft_agents_a365_observability_core-0.2.1.dev43}/microsoft_agents_a365/observability/core/middleware/baggage_builder.py +0 -0
- {microsoft_agents_a365_observability_core-0.2.1.dev36 → microsoft_agents_a365_observability_core-0.2.1.dev43}/microsoft_agents_a365/observability/core/models/__init__.py +0 -0
- {microsoft_agents_a365_observability_core-0.2.1.dev36 → microsoft_agents_a365_observability_core-0.2.1.dev43}/microsoft_agents_a365/observability/core/models/agent_type.py +0 -0
- {microsoft_agents_a365_observability_core-0.2.1.dev36 → microsoft_agents_a365_observability_core-0.2.1.dev43}/microsoft_agents_a365/observability/core/models/caller_details.py +0 -0
- {microsoft_agents_a365_observability_core-0.2.1.dev36 → microsoft_agents_a365_observability_core-0.2.1.dev43}/microsoft_agents_a365/observability/core/models/operation_source.py +0 -0
- {microsoft_agents_a365_observability_core-0.2.1.dev36 → microsoft_agents_a365_observability_core-0.2.1.dev43}/microsoft_agents_a365/observability/core/models/response.py +0 -0
- {microsoft_agents_a365_observability_core-0.2.1.dev36 → microsoft_agents_a365_observability_core-0.2.1.dev43}/microsoft_agents_a365/observability/core/request.py +0 -0
- {microsoft_agents_a365_observability_core-0.2.1.dev36 → microsoft_agents_a365_observability_core-0.2.1.dev43}/microsoft_agents_a365/observability/core/source_metadata.py +0 -0
- {microsoft_agents_a365_observability_core-0.2.1.dev36 → microsoft_agents_a365_observability_core-0.2.1.dev43}/microsoft_agents_a365/observability/core/spans_scopes/__init__.py +0 -0
- {microsoft_agents_a365_observability_core-0.2.1.dev36 → microsoft_agents_a365_observability_core-0.2.1.dev43}/microsoft_agents_a365/observability/core/tenant_details.py +0 -0
- {microsoft_agents_a365_observability_core-0.2.1.dev36 → microsoft_agents_a365_observability_core-0.2.1.dev43}/microsoft_agents_a365/observability/core/tool_call_details.py +0 -0
- {microsoft_agents_a365_observability_core-0.2.1.dev36 → microsoft_agents_a365_observability_core-0.2.1.dev43}/microsoft_agents_a365/observability/core/tool_type.py +0 -0
- {microsoft_agents_a365_observability_core-0.2.1.dev36 → microsoft_agents_a365_observability_core-0.2.1.dev43}/microsoft_agents_a365/observability/core/trace_processor/__init__.py +0 -0
- {microsoft_agents_a365_observability_core-0.2.1.dev36 → microsoft_agents_a365_observability_core-0.2.1.dev43}/microsoft_agents_a365/observability/core/trace_processor/span_processor.py +0 -0
- {microsoft_agents_a365_observability_core-0.2.1.dev36 → microsoft_agents_a365_observability_core-0.2.1.dev43}/microsoft_agents_a365/observability/core/trace_processor/util.py +0 -0
- {microsoft_agents_a365_observability_core-0.2.1.dev36 → microsoft_agents_a365_observability_core-0.2.1.dev43}/microsoft_agents_a365_observability_core.egg-info/SOURCES.txt +0 -0
- {microsoft_agents_a365_observability_core-0.2.1.dev36 → microsoft_agents_a365_observability_core-0.2.1.dev43}/microsoft_agents_a365_observability_core.egg-info/dependency_links.txt +0 -0
- {microsoft_agents_a365_observability_core-0.2.1.dev36 → microsoft_agents_a365_observability_core-0.2.1.dev43}/microsoft_agents_a365_observability_core.egg-info/requires.txt +0 -0
- {microsoft_agents_a365_observability_core-0.2.1.dev36 → microsoft_agents_a365_observability_core-0.2.1.dev43}/microsoft_agents_a365_observability_core.egg-info/top_level.txt +0 -0
- {microsoft_agents_a365_observability_core-0.2.1.dev36 → microsoft_agents_a365_observability_core-0.2.1.dev43}/pyproject.toml +0 -0
- {microsoft_agents_a365_observability_core-0.2.1.dev36 → microsoft_agents_a365_observability_core-0.2.1.dev43}/setup.cfg +0 -0
- {microsoft_agents_a365_observability_core-0.2.1.dev36 → microsoft_agents_a365_observability_core-0.2.1.dev43}/setup.py +0 -0
|
@@ -33,6 +33,7 @@ from .tenant_details import TenantDetails
|
|
|
33
33
|
from .tool_call_details import ToolCallDetails
|
|
34
34
|
from .tool_type import ToolType
|
|
35
35
|
from .trace_processor.span_processor import SpanProcessor
|
|
36
|
+
from .utils import extract_context_from_headers, get_traceparent
|
|
36
37
|
|
|
37
38
|
__all__ = [
|
|
38
39
|
# Main SDK functions
|
|
@@ -71,6 +72,9 @@ __all__ = [
|
|
|
71
72
|
"ExecutionType",
|
|
72
73
|
"InferenceOperationType",
|
|
73
74
|
"ToolType",
|
|
75
|
+
# Utility functions
|
|
76
|
+
"extract_context_from_headers",
|
|
77
|
+
"get_traceparent",
|
|
74
78
|
# Constants
|
|
75
79
|
# all constants from constants.py are exported via *
|
|
76
80
|
]
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
from datetime import datetime
|
|
5
5
|
|
|
6
|
+
from opentelemetry.context import Context
|
|
6
7
|
from opentelemetry.trace import SpanKind
|
|
7
8
|
|
|
8
9
|
from .agent_details import AgentDetails
|
|
@@ -33,7 +34,7 @@ class ExecuteToolScope(OpenTelemetryScope):
|
|
|
33
34
|
agent_details: AgentDetails,
|
|
34
35
|
tenant_details: TenantDetails,
|
|
35
36
|
request: Request | None = None,
|
|
36
|
-
|
|
37
|
+
parent_context: Context | None = None,
|
|
37
38
|
start_time: datetime | None = None,
|
|
38
39
|
end_time: datetime | None = None,
|
|
39
40
|
span_kind: SpanKind | None = None,
|
|
@@ -45,8 +46,9 @@ class ExecuteToolScope(OpenTelemetryScope):
|
|
|
45
46
|
agent_details: The details of the agent making the call
|
|
46
47
|
tenant_details: The details of the tenant
|
|
47
48
|
request: Optional request details for additional context
|
|
48
|
-
|
|
49
|
-
operation
|
|
49
|
+
parent_context: Optional OpenTelemetry Context used to link this span to an
|
|
50
|
+
upstream operation. Use ``extract_context_from_headers()`` to convert a
|
|
51
|
+
Context from HTTP headers containing W3C traceparent.
|
|
50
52
|
start_time: Optional explicit start time as a datetime object. Useful when
|
|
51
53
|
recording a tool call after execution has already completed.
|
|
52
54
|
end_time: Optional explicit end time as a datetime object. When provided,
|
|
@@ -63,7 +65,7 @@ class ExecuteToolScope(OpenTelemetryScope):
|
|
|
63
65
|
agent_details,
|
|
64
66
|
tenant_details,
|
|
65
67
|
request,
|
|
66
|
-
|
|
68
|
+
parent_context,
|
|
67
69
|
start_time,
|
|
68
70
|
end_time,
|
|
69
71
|
span_kind,
|
|
@@ -75,7 +77,7 @@ class ExecuteToolScope(OpenTelemetryScope):
|
|
|
75
77
|
agent_details: AgentDetails,
|
|
76
78
|
tenant_details: TenantDetails,
|
|
77
79
|
request: Request | None = None,
|
|
78
|
-
|
|
80
|
+
parent_context: Context | None = None,
|
|
79
81
|
start_time: datetime | None = None,
|
|
80
82
|
end_time: datetime | None = None,
|
|
81
83
|
span_kind: SpanKind | None = None,
|
|
@@ -87,8 +89,9 @@ class ExecuteToolScope(OpenTelemetryScope):
|
|
|
87
89
|
agent_details: The details of the agent making the call
|
|
88
90
|
tenant_details: The details of the tenant
|
|
89
91
|
request: Optional request details for additional context
|
|
90
|
-
|
|
91
|
-
operation
|
|
92
|
+
parent_context: Optional OpenTelemetry Context used to link this span to an
|
|
93
|
+
upstream operation. Use ``extract_context_from_headers()`` to convert a
|
|
94
|
+
Context from HTTP headers containing W3C traceparent.
|
|
92
95
|
start_time: Optional explicit start time as a datetime object. Useful when
|
|
93
96
|
recording a tool call after execution has already completed.
|
|
94
97
|
end_time: Optional explicit end time as a datetime object. When provided,
|
|
@@ -103,7 +106,7 @@ class ExecuteToolScope(OpenTelemetryScope):
|
|
|
103
106
|
activity_name=f"{EXECUTE_TOOL_OPERATION_NAME} {details.tool_name}",
|
|
104
107
|
agent_details=agent_details,
|
|
105
108
|
tenant_details=tenant_details,
|
|
106
|
-
|
|
109
|
+
parent_context=parent_context,
|
|
107
110
|
start_time=start_time,
|
|
108
111
|
end_time=end_time,
|
|
109
112
|
)
|
|
@@ -23,6 +23,7 @@ from .utils import (
|
|
|
23
23
|
hex_span_id,
|
|
24
24
|
hex_trace_id,
|
|
25
25
|
kind_name,
|
|
26
|
+
parse_retry_after,
|
|
26
27
|
partition_by_identity,
|
|
27
28
|
status_name,
|
|
28
29
|
truncate_span,
|
|
@@ -79,9 +80,9 @@ class _Agent365Exporter(SpanExporter):
|
|
|
79
80
|
logger.info("No spans with tenant/agent identity found; nothing exported.")
|
|
80
81
|
return SpanExportResult.SUCCESS
|
|
81
82
|
|
|
82
|
-
#
|
|
83
|
+
# Log number of groups and total span count
|
|
83
84
|
total_spans = sum(len(activities) for activities in groups.values())
|
|
84
|
-
logger.
|
|
85
|
+
logger.debug(
|
|
85
86
|
f"Found {len(groups)} identity groups with {total_spans} total spans to export"
|
|
86
87
|
)
|
|
87
88
|
|
|
@@ -98,8 +99,8 @@ class _Agent365Exporter(SpanExporter):
|
|
|
98
99
|
|
|
99
100
|
url = build_export_url(endpoint, agent_id, tenant_id, self._use_s2s_endpoint)
|
|
100
101
|
|
|
101
|
-
#
|
|
102
|
-
logger.
|
|
102
|
+
# Log endpoint details at DEBUG to avoid leaking IDs in production logs
|
|
103
|
+
logger.debug(
|
|
103
104
|
f"Exporting {len(activities)} spans to endpoint: {url} "
|
|
104
105
|
f"(tenant: {tenant_id}, agent: {agent_id})"
|
|
105
106
|
)
|
|
@@ -108,10 +109,16 @@ class _Agent365Exporter(SpanExporter):
|
|
|
108
109
|
try:
|
|
109
110
|
token = self._token_resolver(agent_id, tenant_id)
|
|
110
111
|
if token:
|
|
112
|
+
# Warn if sending bearer token over non-HTTPS connection
|
|
113
|
+
if not url.lower().startswith("https://"):
|
|
114
|
+
logger.warning(
|
|
115
|
+
"Bearer token is being sent over a non-HTTPS connection. "
|
|
116
|
+
"This may expose credentials in transit."
|
|
117
|
+
)
|
|
111
118
|
headers["authorization"] = f"Bearer {token}"
|
|
112
|
-
logger.
|
|
119
|
+
logger.debug(f"Token resolved successfully for agent {agent_id}")
|
|
113
120
|
else:
|
|
114
|
-
logger.
|
|
121
|
+
logger.debug(f"No token returned for agent {agent_id}")
|
|
115
122
|
except Exception as e:
|
|
116
123
|
# If token resolution fails, treat as failure for this group
|
|
117
124
|
logger.error(
|
|
@@ -174,7 +181,7 @@ class _Agent365Exporter(SpanExporter):
|
|
|
174
181
|
|
|
175
182
|
# 2xx => success
|
|
176
183
|
if 200 <= resp.status_code < 300:
|
|
177
|
-
logger.
|
|
184
|
+
logger.debug(
|
|
178
185
|
f"HTTP {resp.status_code} success on attempt {attempt + 1}. "
|
|
179
186
|
f"Correlation ID: {correlation_id}. "
|
|
180
187
|
f"Response: {self._truncate_text(resp.text, 200)}"
|
|
@@ -186,12 +193,19 @@ class _Agent365Exporter(SpanExporter):
|
|
|
186
193
|
|
|
187
194
|
# Retry transient
|
|
188
195
|
if resp.status_code in (408, 429) or 500 <= resp.status_code < 600:
|
|
196
|
+
# Respect Retry-After header for 429 responses
|
|
197
|
+
retry_after = parse_retry_after(resp.headers)
|
|
189
198
|
if attempt < DEFAULT_MAX_RETRIES:
|
|
190
|
-
|
|
199
|
+
if retry_after is not None:
|
|
200
|
+
time.sleep(min(retry_after, 60.0))
|
|
201
|
+
else:
|
|
202
|
+
# Exponential backoff with base 0.5s
|
|
203
|
+
time.sleep(0.5 * (2**attempt))
|
|
191
204
|
continue
|
|
192
205
|
# Final attempt failed
|
|
193
206
|
logger.error(
|
|
194
|
-
f"HTTP {resp.status_code} final failure after
|
|
207
|
+
f"HTTP {resp.status_code} final failure after "
|
|
208
|
+
f"{DEFAULT_MAX_RETRIES + 1} attempts. "
|
|
195
209
|
f"Correlation ID: {correlation_id}. "
|
|
196
210
|
f"Response: {response_text}"
|
|
197
211
|
)
|
|
@@ -206,12 +220,11 @@ class _Agent365Exporter(SpanExporter):
|
|
|
206
220
|
|
|
207
221
|
except requests.RequestException as e:
|
|
208
222
|
if attempt < DEFAULT_MAX_RETRIES:
|
|
209
|
-
|
|
223
|
+
# Exponential backoff with base 0.5s
|
|
224
|
+
time.sleep(0.5 * (2**attempt))
|
|
210
225
|
continue
|
|
211
226
|
# Final attempt failed
|
|
212
|
-
logger.error(
|
|
213
|
-
f"Request failed after {DEFAULT_MAX_RETRIES + 1} attempts with exception: {e}"
|
|
214
|
-
)
|
|
227
|
+
logger.error(f"Request failed after {DEFAULT_MAX_RETRIES + 1} attempts: {e}")
|
|
215
228
|
return False
|
|
216
229
|
return False
|
|
217
230
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# Copyright (c) Microsoft Corporation.
|
|
2
2
|
# Licensed under the MIT License.
|
|
3
3
|
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
4
6
|
import json
|
|
5
7
|
import logging
|
|
6
8
|
import os
|
|
@@ -194,6 +196,13 @@ def get_validated_domain_override() -> str | None:
|
|
|
194
196
|
logger.warning(f"Invalid domain override '{domain_override}': {e}")
|
|
195
197
|
return None
|
|
196
198
|
|
|
199
|
+
# Warn when using insecure HTTP — telemetry data and bearer tokens may be exposed
|
|
200
|
+
if domain_override.lower().startswith("http://"):
|
|
201
|
+
logger.warning(
|
|
202
|
+
"Domain override uses insecure HTTP. Telemetry data (including "
|
|
203
|
+
"bearer tokens) will be transmitted in cleartext."
|
|
204
|
+
)
|
|
205
|
+
|
|
197
206
|
return domain_override
|
|
198
207
|
|
|
199
208
|
|
|
@@ -223,6 +232,31 @@ def build_export_url(
|
|
|
223
232
|
return f"https://{endpoint}{endpoint_path}?api-version=1"
|
|
224
233
|
|
|
225
234
|
|
|
235
|
+
def parse_retry_after(headers: dict[str, str]) -> float | None:
|
|
236
|
+
"""Parse the ``Retry-After`` header value.
|
|
237
|
+
|
|
238
|
+
Only numeric (seconds) values are supported. HTTP-date values
|
|
239
|
+
(e.g. ``Wed, 21 Oct 2025 07:28:00 GMT``) are intentionally ignored
|
|
240
|
+
and treated as absent, falling back to exponential backoff.
|
|
241
|
+
|
|
242
|
+
Args:
|
|
243
|
+
headers: Response headers mapping.
|
|
244
|
+
|
|
245
|
+
Returns:
|
|
246
|
+
The number of seconds to wait, or ``None`` if the header is
|
|
247
|
+
absent, non-numeric, or otherwise invalid.
|
|
248
|
+
"""
|
|
249
|
+
retry_after = headers.get("Retry-After")
|
|
250
|
+
if retry_after is None:
|
|
251
|
+
return None
|
|
252
|
+
try:
|
|
253
|
+
return float(retry_after)
|
|
254
|
+
except (ValueError, TypeError):
|
|
255
|
+
# Intentionally ignore HTTP-date formatted Retry-After values;
|
|
256
|
+
# callers should fall back to exponential backoff.
|
|
257
|
+
return None
|
|
258
|
+
|
|
259
|
+
|
|
226
260
|
def is_agent365_exporter_enabled() -> bool:
|
|
227
261
|
"""Check if Agent 365 exporter is enabled."""
|
|
228
262
|
# Check environment variable
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
from datetime import datetime
|
|
5
5
|
from typing import List
|
|
6
6
|
|
|
7
|
+
from opentelemetry.context import Context
|
|
8
|
+
|
|
7
9
|
from .agent_details import AgentDetails
|
|
8
10
|
from .constants import (
|
|
9
11
|
CHANNEL_LINK_KEY,
|
|
@@ -36,7 +38,7 @@ class InferenceScope(OpenTelemetryScope):
|
|
|
36
38
|
agent_details: AgentDetails,
|
|
37
39
|
tenant_details: TenantDetails,
|
|
38
40
|
request: Request | None = None,
|
|
39
|
-
|
|
41
|
+
parent_context: Context | None = None,
|
|
40
42
|
start_time: datetime | None = None,
|
|
41
43
|
end_time: datetime | None = None,
|
|
42
44
|
) -> "InferenceScope":
|
|
@@ -47,8 +49,9 @@ class InferenceScope(OpenTelemetryScope):
|
|
|
47
49
|
agent_details: The details of the agent making the call
|
|
48
50
|
tenant_details: The details of the tenant
|
|
49
51
|
request: Optional request details for additional context
|
|
50
|
-
|
|
51
|
-
operation
|
|
52
|
+
parent_context: Optional OpenTelemetry Context used to link this span to an
|
|
53
|
+
upstream operation. Use ``extract_context_from_headers()`` to convert a
|
|
54
|
+
Context from HTTP headers containing W3C traceparent.
|
|
52
55
|
start_time: Optional explicit start time as a datetime object.
|
|
53
56
|
end_time: Optional explicit end time as a datetime object.
|
|
54
57
|
|
|
@@ -56,7 +59,7 @@ class InferenceScope(OpenTelemetryScope):
|
|
|
56
59
|
A new InferenceScope instance
|
|
57
60
|
"""
|
|
58
61
|
return InferenceScope(
|
|
59
|
-
details, agent_details, tenant_details, request,
|
|
62
|
+
details, agent_details, tenant_details, request, parent_context, start_time, end_time
|
|
60
63
|
)
|
|
61
64
|
|
|
62
65
|
def __init__(
|
|
@@ -65,7 +68,7 @@ class InferenceScope(OpenTelemetryScope):
|
|
|
65
68
|
agent_details: AgentDetails,
|
|
66
69
|
tenant_details: TenantDetails,
|
|
67
70
|
request: Request | None = None,
|
|
68
|
-
|
|
71
|
+
parent_context: Context | None = None,
|
|
69
72
|
start_time: datetime | None = None,
|
|
70
73
|
end_time: datetime | None = None,
|
|
71
74
|
):
|
|
@@ -76,8 +79,9 @@ class InferenceScope(OpenTelemetryScope):
|
|
|
76
79
|
agent_details: The details of the agent making the call
|
|
77
80
|
tenant_details: The details of the tenant
|
|
78
81
|
request: Optional request details for additional context
|
|
79
|
-
|
|
80
|
-
operation
|
|
82
|
+
parent_context: Optional OpenTelemetry Context used to link this span to an
|
|
83
|
+
upstream operation. Use ``extract_context_from_headers()`` to convert a
|
|
84
|
+
Context from HTTP headers containing W3C traceparent.
|
|
81
85
|
start_time: Optional explicit start time as a datetime object.
|
|
82
86
|
end_time: Optional explicit end time as a datetime object.
|
|
83
87
|
"""
|
|
@@ -88,7 +92,7 @@ class InferenceScope(OpenTelemetryScope):
|
|
|
88
92
|
activity_name=f"{details.operationName.value} {details.model}",
|
|
89
93
|
agent_details=agent_details,
|
|
90
94
|
tenant_details=tenant_details,
|
|
91
|
-
|
|
95
|
+
parent_context=parent_context,
|
|
92
96
|
start_time=start_time,
|
|
93
97
|
end_time=end_time,
|
|
94
98
|
)
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import logging
|
|
7
7
|
from datetime import datetime
|
|
8
8
|
|
|
9
|
+
from opentelemetry.context import Context
|
|
9
10
|
from opentelemetry.trace import SpanKind
|
|
10
11
|
|
|
11
12
|
from .agent_details import AgentDetails
|
|
@@ -50,6 +51,7 @@ class InvokeAgentScope(OpenTelemetryScope):
|
|
|
50
51
|
request: Request | None = None,
|
|
51
52
|
caller_agent_details: AgentDetails | None = None,
|
|
52
53
|
caller_details: CallerDetails | None = None,
|
|
54
|
+
parent_context: Context | None = None,
|
|
53
55
|
start_time: datetime | None = None,
|
|
54
56
|
end_time: datetime | None = None,
|
|
55
57
|
span_kind: SpanKind | None = None,
|
|
@@ -63,6 +65,9 @@ class InvokeAgentScope(OpenTelemetryScope):
|
|
|
63
65
|
request: Optional request details for additional context
|
|
64
66
|
caller_agent_details: Optional details of the caller agent
|
|
65
67
|
caller_details: Optional details of the non-agentic caller
|
|
68
|
+
parent_context: Optional OpenTelemetry Context used to link this span to an
|
|
69
|
+
upstream operation. Use ``extract_context_from_headers()`` to convert a
|
|
70
|
+
Context from HTTP headers containing W3C traceparent.
|
|
66
71
|
start_time: Optional explicit start time as a datetime object.
|
|
67
72
|
end_time: Optional explicit end time as a datetime object.
|
|
68
73
|
span_kind: Optional span kind override. Defaults to ``SpanKind.CLIENT``.
|
|
@@ -77,6 +82,7 @@ class InvokeAgentScope(OpenTelemetryScope):
|
|
|
77
82
|
request,
|
|
78
83
|
caller_agent_details,
|
|
79
84
|
caller_details,
|
|
85
|
+
parent_context,
|
|
80
86
|
start_time,
|
|
81
87
|
end_time,
|
|
82
88
|
span_kind,
|
|
@@ -89,6 +95,7 @@ class InvokeAgentScope(OpenTelemetryScope):
|
|
|
89
95
|
request: Request | None = None,
|
|
90
96
|
caller_agent_details: AgentDetails | None = None,
|
|
91
97
|
caller_details: CallerDetails | None = None,
|
|
98
|
+
parent_context: Context | None = None,
|
|
92
99
|
start_time: datetime | None = None,
|
|
93
100
|
end_time: datetime | None = None,
|
|
94
101
|
span_kind: SpanKind | None = None,
|
|
@@ -101,6 +108,9 @@ class InvokeAgentScope(OpenTelemetryScope):
|
|
|
101
108
|
request: Optional request details for additional context
|
|
102
109
|
caller_agent_details: Optional details of the caller agent
|
|
103
110
|
caller_details: Optional details of the non-agentic caller
|
|
111
|
+
parent_context: Optional OpenTelemetry Context used to link this span to an
|
|
112
|
+
upstream operation. Use ``extract_context_from_headers()`` to convert a
|
|
113
|
+
Context from HTTP headers containing W3C traceparent.
|
|
104
114
|
start_time: Optional explicit start time as a datetime object.
|
|
105
115
|
end_time: Optional explicit end time as a datetime object.
|
|
106
116
|
span_kind: Optional span kind override. Defaults to ``SpanKind.CLIENT``.
|
|
@@ -118,6 +128,7 @@ class InvokeAgentScope(OpenTelemetryScope):
|
|
|
118
128
|
activity_name=activity_name,
|
|
119
129
|
agent_details=invoke_agent_details.details,
|
|
120
130
|
tenant_details=tenant_details,
|
|
131
|
+
parent_context=parent_context,
|
|
121
132
|
start_time=start_time,
|
|
122
133
|
end_time=end_time,
|
|
123
134
|
)
|
|
@@ -9,7 +9,9 @@ from datetime import datetime
|
|
|
9
9
|
from threading import Lock
|
|
10
10
|
from typing import TYPE_CHECKING, Any
|
|
11
11
|
|
|
12
|
-
from opentelemetry import
|
|
12
|
+
from opentelemetry import context, trace
|
|
13
|
+
from opentelemetry.context import Context
|
|
14
|
+
from opentelemetry.propagate import inject
|
|
13
15
|
from opentelemetry.trace import (
|
|
14
16
|
Span,
|
|
15
17
|
SpanKind,
|
|
@@ -43,7 +45,7 @@ from .constants import (
|
|
|
43
45
|
TELEMETRY_SDK_VERSION_KEY,
|
|
44
46
|
TENANT_ID_KEY,
|
|
45
47
|
)
|
|
46
|
-
from .utils import get_sdk_version
|
|
48
|
+
from .utils import get_sdk_version
|
|
47
49
|
|
|
48
50
|
if TYPE_CHECKING:
|
|
49
51
|
from .agent_details import AgentDetails
|
|
@@ -97,7 +99,7 @@ class OpenTelemetryScope:
|
|
|
97
99
|
activity_name: str,
|
|
98
100
|
agent_details: "AgentDetails | None" = None,
|
|
99
101
|
tenant_details: "TenantDetails | None" = None,
|
|
100
|
-
|
|
102
|
+
parent_context: Context | None = None,
|
|
101
103
|
start_time: datetime | None = None,
|
|
102
104
|
end_time: datetime | None = None,
|
|
103
105
|
):
|
|
@@ -111,8 +113,9 @@ class OpenTelemetryScope:
|
|
|
111
113
|
activity_name: The name of the activity for display purposes
|
|
112
114
|
agent_details: Optional agent details
|
|
113
115
|
tenant_details: Optional tenant details
|
|
114
|
-
|
|
115
|
-
operation
|
|
116
|
+
parent_context: Optional OpenTelemetry Context used to link this span to an
|
|
117
|
+
upstream operation. Use ``extract_context_from_headers()`` to extract a
|
|
118
|
+
Context from HTTP headers containing W3C traceparent.
|
|
116
119
|
start_time: Optional explicit start time as a datetime object.
|
|
117
120
|
Useful when recording an operation after it has already completed.
|
|
118
121
|
end_time: Optional explicit end time as a datetime object.
|
|
@@ -146,9 +149,8 @@ class OpenTelemetryScope:
|
|
|
146
149
|
activity_kind = SpanKind.CONSUMER
|
|
147
150
|
|
|
148
151
|
# Get context for parent relationship
|
|
149
|
-
# If
|
|
152
|
+
# If parent_context is provided, use it directly
|
|
150
153
|
# Otherwise, use the current context
|
|
151
|
-
parent_context = parse_parent_id_to_context(parent_id)
|
|
152
154
|
span_context = parent_context if parent_context else context.get_current()
|
|
153
155
|
|
|
154
156
|
# Convert custom start time to OTel-compatible format (nanoseconds since epoch)
|
|
@@ -241,21 +243,6 @@ class OpenTelemetryScope:
|
|
|
241
243
|
if value is not None and self._span and self._is_telemetry_enabled():
|
|
242
244
|
self._span.set_attribute(name, value)
|
|
243
245
|
|
|
244
|
-
def add_baggage(self, key: str, value: str) -> None:
|
|
245
|
-
"""Add baggage to the current context.
|
|
246
|
-
|
|
247
|
-
Args:
|
|
248
|
-
key: The baggage key
|
|
249
|
-
value: The baggage value
|
|
250
|
-
"""
|
|
251
|
-
# Set baggage in the current context
|
|
252
|
-
if self._is_telemetry_enabled():
|
|
253
|
-
# Set baggage on the current context
|
|
254
|
-
# This will be inherited by child spans created within this context
|
|
255
|
-
baggage_context = baggage.set_baggage(key, value)
|
|
256
|
-
# The context needs to be made current for child spans to inherit the baggage
|
|
257
|
-
context.attach(baggage_context)
|
|
258
|
-
|
|
259
246
|
def record_attributes(self, attributes: dict[str, Any] | list[tuple[str, Any]]) -> None:
|
|
260
247
|
"""Record multiple attribute key/value pairs for telemetry tracking.
|
|
261
248
|
|
|
@@ -301,6 +288,49 @@ class OpenTelemetryScope:
|
|
|
301
288
|
else:
|
|
302
289
|
self._span.end()
|
|
303
290
|
|
|
291
|
+
def get_context(self) -> Context | None:
|
|
292
|
+
"""Get the OpenTelemetry context for this scope's span.
|
|
293
|
+
|
|
294
|
+
This method returns a Context object containing this scope's span,
|
|
295
|
+
which can be used to propagate trace context to child operations
|
|
296
|
+
or downstream services.
|
|
297
|
+
|
|
298
|
+
Returns:
|
|
299
|
+
A Context containing this scope's span, or None if telemetry
|
|
300
|
+
is disabled or no span exists.
|
|
301
|
+
"""
|
|
302
|
+
if self._span and self._is_telemetry_enabled():
|
|
303
|
+
return set_span_in_context(self._span)
|
|
304
|
+
return None
|
|
305
|
+
|
|
306
|
+
def inject_context_to_headers(self) -> dict[str, str]:
|
|
307
|
+
"""Inject this span's trace context into W3C HTTP headers.
|
|
308
|
+
|
|
309
|
+
Returns a dictionary of headers containing ``traceparent`` and
|
|
310
|
+
optionally ``tracestate`` that can be forwarded to downstream services
|
|
311
|
+
or stored for later context propagation.
|
|
312
|
+
|
|
313
|
+
Example usage:
|
|
314
|
+
|
|
315
|
+
.. code-block:: python
|
|
316
|
+
|
|
317
|
+
scope = OpenTelemetryScope(...)
|
|
318
|
+
headers = scope.inject_context_to_headers()
|
|
319
|
+
# Add headers to outgoing HTTP request
|
|
320
|
+
requests.get("https://downstream-service/api", headers=headers)
|
|
321
|
+
|
|
322
|
+
Returns:
|
|
323
|
+
A dictionary containing W3C trace context headers. Returns an
|
|
324
|
+
empty dictionary if telemetry is disabled or no span exists.
|
|
325
|
+
"""
|
|
326
|
+
headers: dict[str, str] = {}
|
|
327
|
+
if self._span and self._is_telemetry_enabled():
|
|
328
|
+
# Create a context with the current span
|
|
329
|
+
ctx = set_span_in_context(self._span)
|
|
330
|
+
# Use the global propagator to inject trace context into headers
|
|
331
|
+
inject(headers, context=ctx)
|
|
332
|
+
return headers
|
|
333
|
+
|
|
304
334
|
def __enter__(self):
|
|
305
335
|
"""Enter the context manager and make span active."""
|
|
306
336
|
if self._span and self._is_telemetry_enabled():
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
from datetime import datetime
|
|
5
5
|
|
|
6
|
+
from opentelemetry.context import Context
|
|
7
|
+
|
|
6
8
|
from ..agent_details import AgentDetails
|
|
7
9
|
from ..constants import GEN_AI_OUTPUT_MESSAGES_KEY
|
|
8
10
|
from ..models.response import Response
|
|
@@ -16,12 +18,14 @@ OUTPUT_OPERATION_NAME = "output_messages"
|
|
|
16
18
|
class OutputScope(OpenTelemetryScope):
|
|
17
19
|
"""Provides OpenTelemetry tracing scope for output messages."""
|
|
18
20
|
|
|
21
|
+
_MAX_OUTPUT_MESSAGES = 5000
|
|
22
|
+
|
|
19
23
|
@staticmethod
|
|
20
24
|
def start(
|
|
21
25
|
agent_details: AgentDetails,
|
|
22
26
|
tenant_details: TenantDetails,
|
|
23
27
|
response: Response,
|
|
24
|
-
|
|
28
|
+
parent_context: Context | None = None,
|
|
25
29
|
start_time: datetime | None = None,
|
|
26
30
|
end_time: datetime | None = None,
|
|
27
31
|
) -> "OutputScope":
|
|
@@ -31,22 +35,25 @@ class OutputScope(OpenTelemetryScope):
|
|
|
31
35
|
agent_details: The details of the agent
|
|
32
36
|
tenant_details: The details of the tenant
|
|
33
37
|
response: The response details from the agent
|
|
34
|
-
|
|
35
|
-
operation
|
|
38
|
+
parent_context: Optional OpenTelemetry Context used to link this span to an
|
|
39
|
+
upstream operation. Use ``extract_context_from_headers()`` to convert a
|
|
40
|
+
Context from HTTP headers containing W3C traceparent.
|
|
36
41
|
start_time: Optional explicit start time as a datetime object.
|
|
37
42
|
end_time: Optional explicit end time as a datetime object.
|
|
38
43
|
|
|
39
44
|
Returns:
|
|
40
45
|
A new OutputScope instance
|
|
41
46
|
"""
|
|
42
|
-
return OutputScope(
|
|
47
|
+
return OutputScope(
|
|
48
|
+
agent_details, tenant_details, response, parent_context, start_time, end_time
|
|
49
|
+
)
|
|
43
50
|
|
|
44
51
|
def __init__(
|
|
45
52
|
self,
|
|
46
53
|
agent_details: AgentDetails,
|
|
47
54
|
tenant_details: TenantDetails,
|
|
48
55
|
response: Response,
|
|
49
|
-
|
|
56
|
+
parent_context: Context | None = None,
|
|
50
57
|
start_time: datetime | None = None,
|
|
51
58
|
end_time: datetime | None = None,
|
|
52
59
|
):
|
|
@@ -56,8 +63,9 @@ class OutputScope(OpenTelemetryScope):
|
|
|
56
63
|
agent_details: The details of the agent
|
|
57
64
|
tenant_details: The details of the tenant
|
|
58
65
|
response: The response details from the agent
|
|
59
|
-
|
|
60
|
-
operation
|
|
66
|
+
parent_context: Optional OpenTelemetry Context used to link this span to an
|
|
67
|
+
upstream operation. Use ``extract_context_from_headers()`` to convert a
|
|
68
|
+
Context from HTTP headers containing W3C traceparent.
|
|
61
69
|
start_time: Optional explicit start time as a datetime object.
|
|
62
70
|
end_time: Optional explicit end time as a datetime object.
|
|
63
71
|
"""
|
|
@@ -67,7 +75,7 @@ class OutputScope(OpenTelemetryScope):
|
|
|
67
75
|
activity_name=(f"{OUTPUT_OPERATION_NAME} {agent_details.agent_id}"),
|
|
68
76
|
agent_details=agent_details,
|
|
69
77
|
tenant_details=tenant_details,
|
|
70
|
-
|
|
78
|
+
parent_context=parent_context,
|
|
71
79
|
start_time=start_time,
|
|
72
80
|
end_time=end_time,
|
|
73
81
|
)
|
|
@@ -82,9 +90,12 @@ class OutputScope(OpenTelemetryScope):
|
|
|
82
90
|
"""Records the output messages for telemetry tracking.
|
|
83
91
|
|
|
84
92
|
Appends the provided messages to the accumulated output messages list.
|
|
93
|
+
The list is capped at _MAX_OUTPUT_MESSAGES to prevent unbounded memory growth.
|
|
85
94
|
|
|
86
95
|
Args:
|
|
87
96
|
messages: List of output messages to append
|
|
88
97
|
"""
|
|
89
98
|
self._output_messages.extend(messages)
|
|
99
|
+
if len(self._output_messages) > self._MAX_OUTPUT_MESSAGES:
|
|
100
|
+
self._output_messages = self._output_messages[-self._MAX_OUTPUT_MESSAGES :]
|
|
90
101
|
self.set_tag_maybe(GEN_AI_OUTPUT_MESSAGES_KEY, safe_json_dumps(self._output_messages))
|
|
@@ -15,11 +15,12 @@ from threading import RLock
|
|
|
15
15
|
from typing import Any, Generic, TypeVar, cast
|
|
16
16
|
|
|
17
17
|
from opentelemetry import context
|
|
18
|
+
from opentelemetry.propagate import extract
|
|
18
19
|
from opentelemetry.semconv.attributes.exception_attributes import (
|
|
19
20
|
EXCEPTION_MESSAGE,
|
|
20
21
|
EXCEPTION_STACKTRACE,
|
|
21
22
|
)
|
|
22
|
-
from opentelemetry.trace import
|
|
23
|
+
from opentelemetry.trace import Span
|
|
23
24
|
from opentelemetry.util.types import AttributeValue
|
|
24
25
|
from wrapt import ObjectProxy
|
|
25
26
|
|
|
@@ -29,126 +30,58 @@ logger = logging.getLogger(__name__)
|
|
|
29
30
|
logger.addHandler(logging.NullHandler())
|
|
30
31
|
|
|
31
32
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
W3C_TRACE_ID_LENGTH = 32 # 32 hex chars = 128 bits
|
|
35
|
-
W3C_SPAN_ID_LENGTH = 16 # 16 hex chars = 64 bits
|
|
33
|
+
def extract_context_from_headers(headers: dict[str, str]) -> context.Context:
|
|
34
|
+
"""Extract an OpenTelemetry Context from W3C trace HTTP headers.
|
|
36
35
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
Parses ``traceparent`` (and optionally ``tracestate``) headers and returns
|
|
37
|
+
an OpenTelemetry Context that can be passed as ``parent_context`` to any
|
|
38
|
+
scope's ``start()`` method.
|
|
40
39
|
|
|
41
40
|
Args:
|
|
42
|
-
|
|
41
|
+
headers: Dictionary of HTTP headers containing trace context.
|
|
42
|
+
Expected keys include ``traceparent`` and optionally ``tracestate``.
|
|
43
43
|
|
|
44
44
|
Returns:
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
return version == W3C_TRACE_CONTEXT_VERSION
|
|
45
|
+
An OpenTelemetry Context containing the extracted trace information.
|
|
46
|
+
If no valid trace context is found, returns an empty context.
|
|
48
47
|
|
|
48
|
+
Example::
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
"""Check if a string contains only valid hexadecimal characters.
|
|
52
|
-
|
|
53
|
-
Args:
|
|
54
|
-
hex_string: The string to validate
|
|
50
|
+
.. code-block:: python
|
|
55
51
|
|
|
56
|
-
|
|
57
|
-
|
|
52
|
+
headers = {
|
|
53
|
+
"traceparent": "00-1234567890abcdef1234567890abcdef-abcdefabcdef1234-01"
|
|
54
|
+
}
|
|
55
|
+
parent_context = extract_context_from_headers(headers)
|
|
56
|
+
with InferenceScope.start(
|
|
57
|
+
details, agent, tenant, parent_context=parent_context
|
|
58
|
+
):
|
|
59
|
+
pass
|
|
58
60
|
"""
|
|
59
|
-
return
|
|
61
|
+
return extract(headers)
|
|
60
62
|
|
|
61
63
|
|
|
62
|
-
def
|
|
63
|
-
"""
|
|
64
|
+
def get_traceparent(headers: dict[str, str]) -> str | None:
|
|
65
|
+
"""Return the W3C ``traceparent`` value from a headers dictionary.
|
|
64
66
|
|
|
65
67
|
Args:
|
|
66
|
-
|
|
68
|
+
headers: Dictionary of HTTP headers, typically obtained from
|
|
69
|
+
:meth:`OpenTelemetryScope.inject_context_to_headers`.
|
|
67
70
|
|
|
68
71
|
Returns:
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
+
The traceparent string (e.g.
|
|
73
|
+
``"00-<trace-id>-<span-id>-<flags>"``), or ``None`` if the
|
|
74
|
+
key is not present.
|
|
72
75
|
|
|
76
|
+
Example::
|
|
73
77
|
|
|
74
|
-
|
|
75
|
-
"""Validate W3C Trace Context span_id format.
|
|
76
|
-
|
|
77
|
-
Args:
|
|
78
|
-
span_id_hex: The span_id hex string to validate (should be 16 hex chars)
|
|
78
|
+
.. code-block:: python
|
|
79
79
|
|
|
80
|
-
|
|
81
|
-
|
|
80
|
+
# Extract traceparent from incoming HTTP request headers
|
|
81
|
+
traceparent = get_traceparent(request.headers)
|
|
82
|
+
turn_context.turn_state[A365_PARENT_TRACEPARENT_KEY] = traceparent
|
|
82
83
|
"""
|
|
83
|
-
return
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
def parse_parent_id_to_context(parent_id: str | None) -> context.Context | None:
|
|
87
|
-
"""Parse a W3C trace context parent ID and return a context with the parent span.
|
|
88
|
-
|
|
89
|
-
The parent_id format is expected to be W3C Trace Context format:
|
|
90
|
-
"00-{trace_id}-{span_id}-{trace_flags}"
|
|
91
|
-
Example: "00-1234567890abcdef1234567890abcdef-abcdefabcdef1234-01"
|
|
92
|
-
|
|
93
|
-
Args:
|
|
94
|
-
parent_id: The W3C Trace Context format parent ID string
|
|
95
|
-
|
|
96
|
-
Returns:
|
|
97
|
-
A context containing the parent span, or None if parent_id is invalid
|
|
98
|
-
"""
|
|
99
|
-
if not parent_id:
|
|
100
|
-
return None
|
|
101
|
-
|
|
102
|
-
try:
|
|
103
|
-
# W3C Trace Context format: "00-{trace_id}-{span_id}-{trace_flags}"
|
|
104
|
-
parts = parent_id.split("-")
|
|
105
|
-
if len(parts) != 4:
|
|
106
|
-
logger.warning(f"Invalid parent_id format (expected 4 parts): {parent_id}")
|
|
107
|
-
return None
|
|
108
|
-
|
|
109
|
-
version, trace_id_hex, span_id_hex, trace_flags_hex = parts
|
|
110
|
-
|
|
111
|
-
# Validate W3C Trace Context version
|
|
112
|
-
if not validate_w3c_trace_context_version(version):
|
|
113
|
-
logger.warning(f"Unsupported W3C Trace Context version: {version}")
|
|
114
|
-
return None
|
|
115
|
-
|
|
116
|
-
# Validate trace_id (must be 32 hex chars)
|
|
117
|
-
if not validate_trace_id(trace_id_hex):
|
|
118
|
-
logger.warning(
|
|
119
|
-
f"Invalid trace_id (expected {W3C_TRACE_ID_LENGTH} hex chars): '{trace_id_hex}'"
|
|
120
|
-
)
|
|
121
|
-
return None
|
|
122
|
-
|
|
123
|
-
# Validate span_id (must be 16 hex chars)
|
|
124
|
-
if not validate_span_id(span_id_hex):
|
|
125
|
-
logger.warning(
|
|
126
|
-
f"Invalid span_id (expected {W3C_SPAN_ID_LENGTH} hex chars): '{span_id_hex}'"
|
|
127
|
-
)
|
|
128
|
-
return None
|
|
129
|
-
|
|
130
|
-
# Parse the hex values
|
|
131
|
-
trace_id = int(trace_id_hex, 16)
|
|
132
|
-
span_id = int(span_id_hex, 16)
|
|
133
|
-
trace_flags = TraceFlags(int(trace_flags_hex, 16))
|
|
134
|
-
|
|
135
|
-
# Create a SpanContext from the parsed values
|
|
136
|
-
parent_span_context = SpanContext(
|
|
137
|
-
trace_id=trace_id,
|
|
138
|
-
span_id=span_id,
|
|
139
|
-
is_remote=True,
|
|
140
|
-
trace_flags=trace_flags,
|
|
141
|
-
)
|
|
142
|
-
|
|
143
|
-
# Create a NonRecordingSpan with the parent context
|
|
144
|
-
parent_span = NonRecordingSpan(parent_span_context)
|
|
145
|
-
|
|
146
|
-
# Create a context with the parent span
|
|
147
|
-
return set_span_in_context(parent_span)
|
|
148
|
-
|
|
149
|
-
except (ValueError, IndexError) as e:
|
|
150
|
-
logger.warning(f"Failed to parse parent_id '{parent_id}': {e}")
|
|
151
|
-
return None
|
|
84
|
+
return headers.get("traceparent")
|
|
152
85
|
|
|
153
86
|
|
|
154
87
|
def safe_json_dumps(obj: Any, **kwargs: Any) -> str:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|