telemetry-dev 0.2.6__tar.gz → 0.2.8__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.
- {telemetry_dev-0.2.6 → telemetry_dev-0.2.8}/PKG-INFO +11 -6
- {telemetry_dev-0.2.6 → telemetry_dev-0.2.8}/README.md +7 -2
- {telemetry_dev-0.2.6 → telemetry_dev-0.2.8}/pyproject.toml +4 -4
- {telemetry_dev-0.2.6 → telemetry_dev-0.2.8}/pyproject.toml.orig +4 -4
- {telemetry_dev-0.2.6 → telemetry_dev-0.2.8}/src/telemetry_dev/_capture.py +9 -7
- {telemetry_dev-0.2.6 → telemetry_dev-0.2.8}/src/telemetry_dev/_client.py +21 -8
- {telemetry_dev-0.2.6 → telemetry_dev-0.2.8}/src/telemetry_dev/_metrics.py +21 -0
- {telemetry_dev-0.2.6 → telemetry_dev-0.2.8}/src/telemetry_dev/_serialize.py +3 -3
- {telemetry_dev-0.2.6 → telemetry_dev-0.2.8}/src/telemetry_dev/_spans.py +18 -1
- {telemetry_dev-0.2.6 → telemetry_dev-0.2.8}/src/telemetry_dev/otel.py +2 -4
- {telemetry_dev-0.2.6 → telemetry_dev-0.2.8}/LICENSE +0 -0
- {telemetry_dev-0.2.6 → telemetry_dev-0.2.8}/src/telemetry_dev/__init__.py +0 -0
- {telemetry_dev-0.2.6 → telemetry_dev-0.2.8}/src/telemetry_dev/_config.py +0 -0
- {telemetry_dev-0.2.6 → telemetry_dev-0.2.8}/src/telemetry_dev/_context.py +0 -0
- {telemetry_dev-0.2.6 → telemetry_dev-0.2.8}/src/telemetry_dev/_logs.py +0 -0
- {telemetry_dev-0.2.6 → telemetry_dev-0.2.8}/src/telemetry_dev/_observe.py +0 -0
- {telemetry_dev-0.2.6 → telemetry_dev-0.2.8}/src/telemetry_dev/_processor.py +0 -0
- {telemetry_dev-0.2.6 → telemetry_dev-0.2.8}/src/telemetry_dev/_semconv.py +0 -0
- {telemetry_dev-0.2.6 → telemetry_dev-0.2.8}/src/telemetry_dev/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: telemetry-dev
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.8
|
|
4
4
|
Summary: telemetry.dev SDK for Python — OpenTelemetry-native GenAI tracing, logs, and metrics
|
|
5
5
|
Keywords: telemetry,opentelemetry,llm,genai,tracing,observability
|
|
6
6
|
Author: telemetry.dev
|
|
@@ -14,9 +14,9 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.12
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.13
|
|
16
16
|
Classifier: Typing :: Typed
|
|
17
|
-
Requires-Dist: opentelemetry-api>=1.
|
|
18
|
-
Requires-Dist: opentelemetry-sdk>=1.
|
|
19
|
-
Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.
|
|
17
|
+
Requires-Dist: opentelemetry-api>=1.39.0,<2
|
|
18
|
+
Requires-Dist: opentelemetry-sdk>=1.39.0,<2
|
|
19
|
+
Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.39.0,<2
|
|
20
20
|
Requires-Python: >=3.10
|
|
21
21
|
Project-URL: Homepage, https://telemetry.dev
|
|
22
22
|
Project-URL: Repository, https://github.com/telemetry-dev/telemetry
|
|
@@ -159,14 +159,19 @@ the core SDK with provider integrations to enable the new metric.
|
|
|
159
159
|
| `log_level` | `"warn"` | SDK diagnostics level: `debug`/`info`/`warn`/`error`/`silent`. |
|
|
160
160
|
| `capture_input`, `capture_output` | `True` | Global content-capture defaults. |
|
|
161
161
|
| `mask` | `None` | `Callable[[Any, MaskContext], Any]` redaction hook, runs before JSON serialization on input/output/log messages (`MaskContext.key` is the attribute being written). Not applied to correlation identifiers. |
|
|
162
|
-
| `max_attribute_length` | `65536` |
|
|
162
|
+
| `max_attribute_length` | `65536` | Non-negative integer per-content-attribute cap. Invalid values call `on_error`, do not raise into application code, and install a disabled client; truncated values use the ASCII `...[truncated]` marker, or its prefix when the cap is shorter than the marker. The cap also applies, with the marker, to string metadata and log attributes; any other string attribute is cut at the cap without a marker, except that a cap of `0` leaves those at 65536. |
|
|
163
163
|
| `span_filter` | `None` | Export predicate `Callable[[ReadableSpan], bool]`. |
|
|
164
164
|
| `sampler` | OTel environment configuration | OTel `Sampler` that overrides environment settings. Session roots use its root policy. Real parents keep their sampling decisions. |
|
|
165
|
-
| `on_error` | `None` | Receives
|
|
165
|
+
| `on_error` | `None` | Receives internal SDK errors; repeated instrumentation failures from one stream may be coalesced into one report. The SDK never raises them into application code. |
|
|
166
166
|
| `disable_atexit` | `False` | Skip the automatic atexit shutdown. |
|
|
167
167
|
| `timeout` | `10.0` | OTLP HTTP timeout in seconds. |
|
|
168
168
|
| `span_exporter`, `log_exporter`, `metric_reader` | `None` | Test seams / offline mode; any of them enables the client without an API key. |
|
|
169
169
|
|
|
170
|
+
`capture_input=False` and `capture_output=False` omit captured content. Provider integrations may
|
|
171
|
+
still record non-content metadata such as response IDs and models, finish reasons, usage, timing,
|
|
172
|
+
and errors. Capture flags do not gate stop sequences, tool descriptions and definitions (`gen_ai.tool.description`, `gen_ai.tool.definitions`), caller-supplied metadata or raw attributes,
|
|
173
|
+
or exception messages and stack traces; redact those separately when needed.
|
|
174
|
+
|
|
170
175
|
Session roots share trace IDs from the API key and session ID. The hash matches the TypeScript SDK.
|
|
171
176
|
The SDK uses the configured OTel sampling policy, not the synthetic parent's flags.
|
|
172
177
|
For example, `init(sampler=ParentBased(TraceIdRatioBased(0.1)))` samples sessions with OTel's ratio sampler.
|
|
@@ -135,14 +135,19 @@ the core SDK with provider integrations to enable the new metric.
|
|
|
135
135
|
| `log_level` | `"warn"` | SDK diagnostics level: `debug`/`info`/`warn`/`error`/`silent`. |
|
|
136
136
|
| `capture_input`, `capture_output` | `True` | Global content-capture defaults. |
|
|
137
137
|
| `mask` | `None` | `Callable[[Any, MaskContext], Any]` redaction hook, runs before JSON serialization on input/output/log messages (`MaskContext.key` is the attribute being written). Not applied to correlation identifiers. |
|
|
138
|
-
| `max_attribute_length` | `65536` |
|
|
138
|
+
| `max_attribute_length` | `65536` | Non-negative integer per-content-attribute cap. Invalid values call `on_error`, do not raise into application code, and install a disabled client; truncated values use the ASCII `...[truncated]` marker, or its prefix when the cap is shorter than the marker. The cap also applies, with the marker, to string metadata and log attributes; any other string attribute is cut at the cap without a marker, except that a cap of `0` leaves those at 65536. |
|
|
139
139
|
| `span_filter` | `None` | Export predicate `Callable[[ReadableSpan], bool]`. |
|
|
140
140
|
| `sampler` | OTel environment configuration | OTel `Sampler` that overrides environment settings. Session roots use its root policy. Real parents keep their sampling decisions. |
|
|
141
|
-
| `on_error` | `None` | Receives
|
|
141
|
+
| `on_error` | `None` | Receives internal SDK errors; repeated instrumentation failures from one stream may be coalesced into one report. The SDK never raises them into application code. |
|
|
142
142
|
| `disable_atexit` | `False` | Skip the automatic atexit shutdown. |
|
|
143
143
|
| `timeout` | `10.0` | OTLP HTTP timeout in seconds. |
|
|
144
144
|
| `span_exporter`, `log_exporter`, `metric_reader` | `None` | Test seams / offline mode; any of them enables the client without an API key. |
|
|
145
145
|
|
|
146
|
+
`capture_input=False` and `capture_output=False` omit captured content. Provider integrations may
|
|
147
|
+
still record non-content metadata such as response IDs and models, finish reasons, usage, timing,
|
|
148
|
+
and errors. Capture flags do not gate stop sequences, tool descriptions and definitions (`gen_ai.tool.description`, `gen_ai.tool.definitions`), caller-supplied metadata or raw attributes,
|
|
149
|
+
or exception messages and stack traces; redact those separately when needed.
|
|
150
|
+
|
|
146
151
|
Session roots share trace IDs from the API key and session ID. The hash matches the TypeScript SDK.
|
|
147
152
|
The SDK uses the configured OTel sampling policy, not the synthetic parent's flags.
|
|
148
153
|
For example, `init(sampler=ParentBased(TraceIdRatioBased(0.1)))` samples sessions with OTel's ratio sampler.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "telemetry-dev"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.8"
|
|
4
4
|
description = "telemetry.dev SDK for Python — OpenTelemetry-native GenAI tracing, logs, and metrics"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "MIT"
|
|
@@ -25,9 +25,9 @@ classifiers = [
|
|
|
25
25
|
"Typing :: Typed",
|
|
26
26
|
]
|
|
27
27
|
dependencies = [
|
|
28
|
-
"opentelemetry-api>=1.
|
|
29
|
-
"opentelemetry-sdk>=1.
|
|
30
|
-
"opentelemetry-exporter-otlp-proto-http>=1.
|
|
28
|
+
"opentelemetry-api>=1.39.0,<2",
|
|
29
|
+
"opentelemetry-sdk>=1.39.0,<2",
|
|
30
|
+
"opentelemetry-exporter-otlp-proto-http>=1.39.0,<2",
|
|
31
31
|
]
|
|
32
32
|
|
|
33
33
|
[[project.authors]]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "telemetry-dev"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.8"
|
|
4
4
|
description = "telemetry.dev SDK for Python — OpenTelemetry-native GenAI tracing, logs, and metrics"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "MIT"
|
|
@@ -19,9 +19,9 @@ classifiers = [
|
|
|
19
19
|
"Typing :: Typed",
|
|
20
20
|
]
|
|
21
21
|
dependencies = [
|
|
22
|
-
"opentelemetry-api>=1.
|
|
23
|
-
"opentelemetry-sdk>=1.
|
|
24
|
-
"opentelemetry-exporter-otlp-proto-http>=1.
|
|
22
|
+
"opentelemetry-api>=1.39.0,<2",
|
|
23
|
+
"opentelemetry-sdk>=1.39.0,<2",
|
|
24
|
+
"opentelemetry-exporter-otlp-proto-http>=1.39.0,<2",
|
|
25
25
|
]
|
|
26
26
|
|
|
27
27
|
[project.urls]
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
from collections.abc import Iterable
|
|
5
|
+
from collections.abc import Iterable, Mapping, Sequence
|
|
6
6
|
from itertools import chain
|
|
7
7
|
from typing import Final, cast
|
|
8
8
|
|
|
@@ -123,16 +123,18 @@ class CaptureBudget:
|
|
|
123
123
|
return byte_count, item_count
|
|
124
124
|
seen.add(value_id)
|
|
125
125
|
try:
|
|
126
|
-
mapping:
|
|
126
|
+
mapping: Mapping[object, object] | None = None
|
|
127
127
|
children: Iterable[object] = ()
|
|
128
|
-
if isinstance(value,
|
|
129
|
-
mapping = cast("
|
|
130
|
-
elif isinstance(value,
|
|
128
|
+
if isinstance(value, Mapping):
|
|
129
|
+
mapping = cast("Mapping[object, object]", value)
|
|
130
|
+
elif isinstance(value, Sequence) and not isinstance(value, str | bytes | bytearray):
|
|
131
|
+
children = cast("Iterable[object]", value)
|
|
132
|
+
elif isinstance(value, set | frozenset):
|
|
131
133
|
children = cast("Iterable[object]", value)
|
|
132
134
|
else:
|
|
133
135
|
attributes = getattr(value, "__dict__", None)
|
|
134
|
-
if isinstance(attributes,
|
|
135
|
-
mapping = cast("
|
|
136
|
+
if isinstance(attributes, Mapping):
|
|
137
|
+
mapping = cast("Mapping[object, object]", attributes)
|
|
136
138
|
|
|
137
139
|
if mapping is not None:
|
|
138
140
|
children = chain.from_iterable(mapping.items())
|
|
@@ -41,10 +41,15 @@ from ._config import (
|
|
|
41
41
|
resolve_config,
|
|
42
42
|
)
|
|
43
43
|
from ._context import SessionSampler
|
|
44
|
-
from ._metrics import
|
|
44
|
+
from ._metrics import (
|
|
45
|
+
GuardedOTLPMetricExporter,
|
|
46
|
+
MetricsRecorder,
|
|
47
|
+
OutputChunkAggregation,
|
|
48
|
+
sdk_meter_provider,
|
|
49
|
+
)
|
|
45
50
|
from ._processor import ExportMode, StampingSpanProcessor
|
|
46
51
|
from ._semconv import SCOPE_NAME
|
|
47
|
-
from ._serialize import Mask, serialize_content
|
|
52
|
+
from ._serialize import DEFAULT_MAX_ATTRIBUTE_LENGTH, Mask, serialize_content
|
|
48
53
|
|
|
49
54
|
_lock = threading.RLock()
|
|
50
55
|
_client: Client | None = None
|
|
@@ -177,14 +182,16 @@ class Client:
|
|
|
177
182
|
)
|
|
178
183
|
|
|
179
184
|
# The funnel keeps capped content (marker included) within max_attribute_length, so
|
|
180
|
-
# the backstop can use the same cap for raw attributes set outside the funnel.
|
|
185
|
+
# the backstop can use the same cap for raw attributes set outside the funnel. A zero
|
|
186
|
+
# cap only drops content; as a backstop it would blank every string attribute.
|
|
187
|
+
backstop_length = max_attribute_length or DEFAULT_MAX_ATTRIBUTE_LENGTH
|
|
181
188
|
self._tracer_provider = TracerProvider(
|
|
182
189
|
resource=resource,
|
|
183
190
|
sampler=sampler,
|
|
184
191
|
shutdown_on_exit=False,
|
|
185
192
|
span_limits=SpanLimits(
|
|
186
|
-
max_attribute_length=
|
|
187
|
-
max_span_attribute_length=
|
|
193
|
+
max_attribute_length=backstop_length,
|
|
194
|
+
max_span_attribute_length=backstop_length,
|
|
188
195
|
),
|
|
189
196
|
)
|
|
190
197
|
self._tracer_provider.sampler = SessionSampler(self._tracer_provider.sampler)
|
|
@@ -207,9 +214,7 @@ class Client:
|
|
|
207
214
|
)
|
|
208
215
|
metrics_recorder: MetricsRecorder | None = None
|
|
209
216
|
if metric_reader is not None:
|
|
210
|
-
self._meter_provider =
|
|
211
|
-
metric_readers=[metric_reader], resource=resource, shutdown_on_exit=False
|
|
212
|
-
)
|
|
217
|
+
self._meter_provider = sdk_meter_provider(metric_reader, resource)
|
|
213
218
|
meter = self._meter_provider.get_meter(SCOPE_NAME, SDK_VERSION)
|
|
214
219
|
metrics_recorder = MetricsRecorder(
|
|
215
220
|
meter, on_error=on_error, output_chunks=self._output_chunks
|
|
@@ -362,6 +367,14 @@ def init(
|
|
|
362
367
|
try:
|
|
363
368
|
if session_mode not in ("explicit", "process"):
|
|
364
369
|
raise ValueError(f"invalid session_mode: {session_mode!r}")
|
|
370
|
+
raw_max_attribute_length: Any = max_attribute_length
|
|
371
|
+
if (
|
|
372
|
+
isinstance(raw_max_attribute_length, bool)
|
|
373
|
+
or not isinstance(raw_max_attribute_length, int)
|
|
374
|
+
or raw_max_attribute_length < 0
|
|
375
|
+
):
|
|
376
|
+
raise ValueError("max_attribute_length must be a non-negative integer")
|
|
377
|
+
max_attribute_length = raw_max_attribute_length
|
|
365
378
|
client = Client(
|
|
366
379
|
config=config,
|
|
367
380
|
enabled=effective_enabled,
|
|
@@ -9,16 +9,20 @@ from typing import Any
|
|
|
9
9
|
|
|
10
10
|
from opentelemetry.exporter.otlp.proto.http.metric_exporter import OTLPMetricExporter
|
|
11
11
|
from opentelemetry.metrics import Meter
|
|
12
|
+
from opentelemetry.sdk.metrics import MeterProvider
|
|
12
13
|
from opentelemetry.sdk.metrics.export import (
|
|
13
14
|
AggregationTemporality,
|
|
14
15
|
Histogram,
|
|
15
16
|
HistogramDataPoint,
|
|
16
17
|
Metric,
|
|
17
18
|
MetricExportResult,
|
|
19
|
+
MetricReader,
|
|
18
20
|
MetricsData,
|
|
19
21
|
ResourceMetrics,
|
|
20
22
|
ScopeMetrics,
|
|
21
23
|
)
|
|
24
|
+
from opentelemetry.sdk.metrics.view import DropAggregation, View
|
|
25
|
+
from opentelemetry.sdk.resources import Resource
|
|
22
26
|
from opentelemetry.sdk.trace import ReadableSpan
|
|
23
27
|
from opentelemetry.trace import INVALID_SPAN_CONTEXT, NonRecordingSpan, set_span_in_context
|
|
24
28
|
|
|
@@ -41,6 +45,23 @@ _MAX_CHUNK_ATTRIBUTE_SETS = 2000
|
|
|
41
45
|
_CHUNK_METRIC_NAME = "gen_ai.client.operation.time_per_output_chunk"
|
|
42
46
|
|
|
43
47
|
|
|
48
|
+
def sdk_meter_provider(reader: MetricReader, resource: Resource) -> MeterProvider:
|
|
49
|
+
return MeterProvider(
|
|
50
|
+
metric_readers=[reader],
|
|
51
|
+
resource=resource,
|
|
52
|
+
shutdown_on_exit=False,
|
|
53
|
+
# OpenTelemetry records metric-reader metrics on the reader's own provider (always in
|
|
54
|
+
# 1.41, opt-in from 1.42); they must not be exported to telemetry.dev.
|
|
55
|
+
views=[
|
|
56
|
+
View(
|
|
57
|
+
meter_name="opentelemetry-sdk",
|
|
58
|
+
instrument_name="otel.sdk.*",
|
|
59
|
+
aggregation=DropAggregation(),
|
|
60
|
+
)
|
|
61
|
+
],
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
|
|
44
65
|
@dataclass
|
|
45
66
|
class _ChunkAggregate:
|
|
46
67
|
count: int
|
|
@@ -31,9 +31,9 @@ def truncate(text: str, max_len: int) -> str:
|
|
|
31
31
|
encoded = text.encode("utf-16-le")
|
|
32
32
|
if len(encoded) <= max_len * 2:
|
|
33
33
|
return text
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
head = encoded[:
|
|
34
|
+
if max_len < len(TRUNCATION_MARKER):
|
|
35
|
+
return TRUNCATION_MARKER[: max(max_len, 0)]
|
|
36
|
+
head = encoded[: (max_len - len(TRUNCATION_MARKER)) * 2]
|
|
37
37
|
try:
|
|
38
38
|
return head.decode("utf-16-le") + TRUNCATION_MARKER
|
|
39
39
|
except UnicodeDecodeError:
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import math
|
|
4
|
+
import sys
|
|
4
5
|
import threading
|
|
5
6
|
import time
|
|
6
7
|
import traceback
|
|
@@ -267,7 +268,7 @@ def _fields_to_attributes(
|
|
|
267
268
|
for key, value in raw.items():
|
|
268
269
|
attr = coerce_attr_value(
|
|
269
270
|
value,
|
|
270
|
-
max_len=
|
|
271
|
+
max_len=sys.maxsize,
|
|
271
272
|
key=key,
|
|
272
273
|
on_error=client.on_error,
|
|
273
274
|
)
|
|
@@ -379,6 +380,22 @@ class SpanHandle:
|
|
|
379
380
|
self._context_token: Token[Context] | None = None
|
|
380
381
|
self._ended = False
|
|
381
382
|
|
|
383
|
+
@property
|
|
384
|
+
def capture_output(self) -> bool:
|
|
385
|
+
return self._state.capture_output if self._state is not None else False
|
|
386
|
+
|
|
387
|
+
@property
|
|
388
|
+
def capture_masked(self) -> bool:
|
|
389
|
+
return self._client is not None and self._client.mask is not None
|
|
390
|
+
|
|
391
|
+
@property
|
|
392
|
+
def max_attribute_length(self) -> int:
|
|
393
|
+
return self._client.max_attribute_length if self._client is not None else 0
|
|
394
|
+
|
|
395
|
+
def report_error(self, cause: BaseException) -> None:
|
|
396
|
+
if self._client is not None:
|
|
397
|
+
self._client.report("provider instrumentation failed", cause)
|
|
398
|
+
|
|
382
399
|
def _recording(self) -> bool:
|
|
383
400
|
return (
|
|
384
401
|
self._client is not None
|
|
@@ -22,7 +22,7 @@ from opentelemetry.sdk.trace import ReadableSpan, Span, SpanProcessor
|
|
|
22
22
|
from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult
|
|
23
23
|
|
|
24
24
|
from ._config import SDK_VERSION, logger, report_error, resolve_config
|
|
25
|
-
from ._metrics import GuardedOTLPMetricExporter, MetricsRecorder
|
|
25
|
+
from ._metrics import GuardedOTLPMetricExporter, MetricsRecorder, sdk_meter_provider
|
|
26
26
|
from ._processor import ExportMode, StampingSpanProcessor
|
|
27
27
|
from ._semconv import SCOPE_NAME
|
|
28
28
|
|
|
@@ -137,9 +137,7 @@ class TelemetrySpanProcessor(SpanProcessor):
|
|
|
137
137
|
else _DORMANT_METRIC_INTERVAL_MILLIS
|
|
138
138
|
),
|
|
139
139
|
)
|
|
140
|
-
self._meter_provider =
|
|
141
|
-
metric_readers=[reader], resource=resource, shutdown_on_exit=False
|
|
142
|
-
)
|
|
140
|
+
self._meter_provider = sdk_meter_provider(reader, resource)
|
|
143
141
|
meter = self._meter_provider.get_meter(SCOPE_NAME, SDK_VERSION)
|
|
144
142
|
metrics_recorder = MetricsRecorder(meter, on_error=on_error)
|
|
145
143
|
|
|
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
|