telemetry-dev 0.2.2__tar.gz → 0.2.3__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.3/LICENSE +21 -0
- {telemetry_dev-0.2.2 → telemetry_dev-0.2.3}/PKG-INFO +9 -2
- {telemetry_dev-0.2.2 → telemetry_dev-0.2.3}/README.md +6 -0
- {telemetry_dev-0.2.2 → telemetry_dev-0.2.3}/pyproject.toml +3 -2
- {telemetry_dev-0.2.2 → telemetry_dev-0.2.3}/pyproject.toml.orig +3 -2
- {telemetry_dev-0.2.2 → telemetry_dev-0.2.3}/src/telemetry_dev/_capture.py +2 -27
- {telemetry_dev-0.2.2 → telemetry_dev-0.2.3}/src/telemetry_dev/_client.py +10 -0
- {telemetry_dev-0.2.2 → telemetry_dev-0.2.3}/src/telemetry_dev/_config.py +1 -0
- {telemetry_dev-0.2.2 → telemetry_dev-0.2.3}/src/telemetry_dev/_context.py +7 -0
- {telemetry_dev-0.2.2 → telemetry_dev-0.2.3}/src/telemetry_dev/_logs.py +16 -4
- {telemetry_dev-0.2.2 → telemetry_dev-0.2.3}/src/telemetry_dev/_spans.py +18 -3
- {telemetry_dev-0.2.2 → telemetry_dev-0.2.3}/src/telemetry_dev/__init__.py +0 -0
- {telemetry_dev-0.2.2 → telemetry_dev-0.2.3}/src/telemetry_dev/_metrics.py +0 -0
- {telemetry_dev-0.2.2 → telemetry_dev-0.2.3}/src/telemetry_dev/_observe.py +0 -0
- {telemetry_dev-0.2.2 → telemetry_dev-0.2.3}/src/telemetry_dev/_processor.py +0 -0
- {telemetry_dev-0.2.2 → telemetry_dev-0.2.3}/src/telemetry_dev/_semconv.py +0 -0
- {telemetry_dev-0.2.2 → telemetry_dev-0.2.3}/src/telemetry_dev/_serialize.py +0 -0
- {telemetry_dev-0.2.2 → telemetry_dev-0.2.3}/src/telemetry_dev/otel.py +0 -0
- {telemetry_dev-0.2.2 → telemetry_dev-0.2.3}/src/telemetry_dev/py.typed +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 telemetry.dev
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: telemetry-dev
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
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
|
|
7
7
|
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
8
9
|
Classifier: Development Status :: 4 - Beta
|
|
9
10
|
Classifier: Intended Audience :: Developers
|
|
10
11
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -18,7 +19,7 @@ Requires-Dist: opentelemetry-sdk>=1.35.0,<2
|
|
|
18
19
|
Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.35.0,<2
|
|
19
20
|
Requires-Python: >=3.10
|
|
20
21
|
Project-URL: Homepage, https://telemetry.dev
|
|
21
|
-
Project-URL: Repository, https://github.com/telemetry-dev/
|
|
22
|
+
Project-URL: Repository, https://github.com/telemetry-dev/sdks
|
|
22
23
|
Description-Content-Type: text/markdown
|
|
23
24
|
|
|
24
25
|
# telemetry-dev
|
|
@@ -85,6 +86,12 @@ routed to the `on_error` hook / `telemetry_dev` logger — never raised into you
|
|
|
85
86
|
|
|
86
87
|
Explicit `init()` arguments take precedence over environment variables.
|
|
87
88
|
|
|
89
|
+
Root spans remain independent by default. `telemetry_dev.init(session_mode="process")` groups
|
|
90
|
+
otherwise uncorrelated spans and logs using a fresh opaque UUID for each enabled SDK
|
|
91
|
+
initialization. Explicit and propagated session IDs take precedence, and valid parent trace
|
|
92
|
+
context is preserved. The generated session ends when the client is shut down or replaced; a
|
|
93
|
+
later `init()` generates a new ID. It is not persisted across processes.
|
|
94
|
+
|
|
88
95
|
## API reference
|
|
89
96
|
|
|
90
97
|
| Name | Description |
|
|
@@ -62,6 +62,12 @@ routed to the `on_error` hook / `telemetry_dev` logger — never raised into you
|
|
|
62
62
|
|
|
63
63
|
Explicit `init()` arguments take precedence over environment variables.
|
|
64
64
|
|
|
65
|
+
Root spans remain independent by default. `telemetry_dev.init(session_mode="process")` groups
|
|
66
|
+
otherwise uncorrelated spans and logs using a fresh opaque UUID for each enabled SDK
|
|
67
|
+
initialization. Explicit and propagated session IDs take precedence, and valid parent trace
|
|
68
|
+
context is preserved. The generated session ends when the client is shut down or replaced; a
|
|
69
|
+
later `init()` generates a new ID. It is not persisted across processes.
|
|
70
|
+
|
|
65
71
|
## API reference
|
|
66
72
|
|
|
67
73
|
| Name | Description |
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "telemetry-dev"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.3"
|
|
4
4
|
description = "telemetry.dev SDK for Python — OpenTelemetry-native GenAI tracing, logs, and metrics"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "MIT"
|
|
7
|
+
license-files = ["LICENSE"]
|
|
7
8
|
requires-python = ">=3.10"
|
|
8
9
|
keywords = [
|
|
9
10
|
"telemetry",
|
|
@@ -34,7 +35,7 @@ name = "telemetry.dev"
|
|
|
34
35
|
|
|
35
36
|
[project.urls]
|
|
36
37
|
Homepage = "https://telemetry.dev"
|
|
37
|
-
Repository = "https://github.com/telemetry-dev/
|
|
38
|
+
Repository = "https://github.com/telemetry-dev/sdks"
|
|
38
39
|
|
|
39
40
|
[dependency-groups]
|
|
40
41
|
dev = [
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "telemetry-dev"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.3"
|
|
4
4
|
description = "telemetry.dev SDK for Python — OpenTelemetry-native GenAI tracing, logs, and metrics"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "MIT"
|
|
7
|
+
license-files = ["LICENSE"]
|
|
7
8
|
requires-python = ">=3.10"
|
|
8
9
|
authors = [{ name = "telemetry.dev" }]
|
|
9
10
|
keywords = ["telemetry", "opentelemetry", "llm", "genai", "tracing", "observability"]
|
|
@@ -25,7 +26,7 @@ dependencies = [
|
|
|
25
26
|
|
|
26
27
|
[project.urls]
|
|
27
28
|
Homepage = "https://telemetry.dev"
|
|
28
|
-
Repository = "https://github.com/telemetry-dev/
|
|
29
|
+
Repository = "https://github.com/telemetry-dev/sdks"
|
|
29
30
|
|
|
30
31
|
[dependency-groups]
|
|
31
32
|
dev = [
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
from collections.abc import Iterable
|
|
6
|
+
from itertools import chain
|
|
6
7
|
from typing import Final, cast
|
|
7
8
|
|
|
8
9
|
_DEFAULT_MAX_BYTES: Final = 64 * 1024
|
|
@@ -133,33 +134,7 @@ class CaptureBudget:
|
|
|
133
134
|
mapping = cast("dict[object, object]", attributes)
|
|
134
135
|
|
|
135
136
|
if mapping is not None:
|
|
136
|
-
|
|
137
|
-
measured = cls._measure(
|
|
138
|
-
key,
|
|
139
|
-
remaining_bytes=remaining_bytes - byte_count,
|
|
140
|
-
remaining_items=remaining_items - item_count,
|
|
141
|
-
depth=depth + 1,
|
|
142
|
-
seen=seen,
|
|
143
|
-
)
|
|
144
|
-
if measured is None:
|
|
145
|
-
return None
|
|
146
|
-
child_bytes, child_items = measured
|
|
147
|
-
byte_count += child_bytes
|
|
148
|
-
item_count += child_items
|
|
149
|
-
|
|
150
|
-
measured = cls._measure(
|
|
151
|
-
child,
|
|
152
|
-
remaining_bytes=remaining_bytes - byte_count,
|
|
153
|
-
remaining_items=remaining_items - item_count,
|
|
154
|
-
depth=depth + 1,
|
|
155
|
-
seen=seen,
|
|
156
|
-
)
|
|
157
|
-
if measured is None:
|
|
158
|
-
return None
|
|
159
|
-
child_bytes, child_items = measured
|
|
160
|
-
byte_count += child_bytes
|
|
161
|
-
item_count += child_items
|
|
162
|
-
return byte_count, item_count
|
|
137
|
+
children = chain.from_iterable(mapping.items())
|
|
163
138
|
|
|
164
139
|
for child in children:
|
|
165
140
|
measured = cls._measure(
|
|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import atexit
|
|
4
4
|
import threading
|
|
5
|
+
import uuid
|
|
5
6
|
from collections.abc import Callable
|
|
6
7
|
from typing import Any
|
|
7
8
|
|
|
@@ -33,6 +34,7 @@ from ._config import (
|
|
|
33
34
|
SDK_VERSION,
|
|
34
35
|
LogLevelOption,
|
|
35
36
|
ResolvedConfig,
|
|
37
|
+
SessionMode,
|
|
36
38
|
configure_logger,
|
|
37
39
|
logger,
|
|
38
40
|
report_error,
|
|
@@ -123,6 +125,7 @@ class Client:
|
|
|
123
125
|
*,
|
|
124
126
|
config: ResolvedConfig,
|
|
125
127
|
enabled: bool,
|
|
128
|
+
session_mode: SessionMode = "explicit",
|
|
126
129
|
register_global: bool = False,
|
|
127
130
|
export_mode: ExportMode = "batched",
|
|
128
131
|
capture_input: bool = True,
|
|
@@ -145,6 +148,9 @@ class Client:
|
|
|
145
148
|
self.mask = mask
|
|
146
149
|
self.max_attribute_length = max_attribute_length
|
|
147
150
|
self.on_error = on_error
|
|
151
|
+
self._process_session_id: str | None = (
|
|
152
|
+
str(uuid.uuid4()) if enabled and session_mode == "process" else None
|
|
153
|
+
)
|
|
148
154
|
self._shutdown = False
|
|
149
155
|
self._atexit_registered = False
|
|
150
156
|
|
|
@@ -316,6 +322,7 @@ def init(
|
|
|
316
322
|
environment: str | None = None,
|
|
317
323
|
service_name: str | None = None,
|
|
318
324
|
enabled: bool = True,
|
|
325
|
+
session_mode: SessionMode = "explicit",
|
|
319
326
|
register_global: bool = False,
|
|
320
327
|
export_mode: ExportMode = "batched",
|
|
321
328
|
log_level: LogLevelOption = "warn",
|
|
@@ -348,9 +355,12 @@ def init(
|
|
|
348
355
|
logger.debug("telemetry-dev disabled: no API key")
|
|
349
356
|
|
|
350
357
|
try:
|
|
358
|
+
if session_mode not in ("explicit", "process"):
|
|
359
|
+
raise ValueError(f"invalid session_mode: {session_mode!r}")
|
|
351
360
|
client = Client(
|
|
352
361
|
config=config,
|
|
353
362
|
enabled=effective_enabled,
|
|
363
|
+
session_mode=session_mode,
|
|
354
364
|
register_global=register_global,
|
|
355
365
|
export_mode=export_mode,
|
|
356
366
|
capture_input=capture_input,
|
|
@@ -78,6 +78,13 @@ def propagated_attributes(context: Context | None = None) -> dict[str, Attribute
|
|
|
78
78
|
return {}
|
|
79
79
|
|
|
80
80
|
|
|
81
|
+
def context_with_propagated_attributes(
|
|
82
|
+
attributes: Mapping[str, AttributeValue], context: Context | None = None
|
|
83
|
+
) -> Context:
|
|
84
|
+
base = context if context is not None else otel_context.get_current()
|
|
85
|
+
return otel_context.set_value(_PROPAGATED_KEY, attributes, context=base)
|
|
86
|
+
|
|
87
|
+
|
|
81
88
|
@contextmanager
|
|
82
89
|
def propagate_attributes(
|
|
83
90
|
*,
|
|
@@ -7,8 +7,8 @@ from opentelemetry._logs import SeverityNumber
|
|
|
7
7
|
|
|
8
8
|
from ._client import get_client
|
|
9
9
|
from ._config import logger
|
|
10
|
-
from ._context import propagated_attributes
|
|
11
|
-
from ._semconv import SEVERITY, LogLevel
|
|
10
|
+
from ._context import context_with_propagated_attributes, propagated_attributes
|
|
11
|
+
from ._semconv import ATTR_SESSION_ID, SEVERITY, LogLevel
|
|
12
12
|
from ._serialize import AttributeValue, coerce_attr_value
|
|
13
13
|
|
|
14
14
|
|
|
@@ -41,9 +41,21 @@ def log(
|
|
|
41
41
|
)
|
|
42
42
|
if attr is not None:
|
|
43
43
|
attrs[key] = attr
|
|
44
|
+
propagated = propagated_attributes()
|
|
45
|
+
process_session_id = client._process_session_id # pyright: ignore[reportPrivateUsage]
|
|
46
|
+
explicit_session = attributes is not None and ATTR_SESSION_ID in attributes
|
|
47
|
+
if (
|
|
48
|
+
not explicit_session
|
|
49
|
+
and ATTR_SESSION_ID not in propagated
|
|
50
|
+
and process_session_id is not None
|
|
51
|
+
):
|
|
52
|
+
propagated[ATTR_SESSION_ID] = process_session_id
|
|
53
|
+
context = otel_context.get_current()
|
|
54
|
+
if propagated:
|
|
55
|
+
context = context_with_propagated_attributes(propagated, context)
|
|
44
56
|
# Propagated correlation attrs win on key collisions, matching the span-processor
|
|
45
57
|
# stamping model and the TypeScript SDK.
|
|
46
|
-
for key, value in
|
|
58
|
+
for key, value in propagated.items():
|
|
47
59
|
attr = coerce_attr_value(
|
|
48
60
|
value,
|
|
49
61
|
max_len=client.max_attribute_length,
|
|
@@ -54,7 +66,7 @@ def log(
|
|
|
54
66
|
attrs[key] = attr
|
|
55
67
|
body = client.serialize(message, "log.message") or ""
|
|
56
68
|
client.otel_logger.emit(
|
|
57
|
-
context=
|
|
69
|
+
context=context,
|
|
58
70
|
severity_number=SeverityNumber(severity),
|
|
59
71
|
severity_text=normalized.upper(),
|
|
60
72
|
body=body,
|
|
@@ -19,6 +19,7 @@ from ._client import Client, get_client
|
|
|
19
19
|
from ._config import logger
|
|
20
20
|
from ._context import (
|
|
21
21
|
context_from_parent,
|
|
22
|
+
context_with_propagated_attributes,
|
|
22
23
|
propagated_attributes,
|
|
23
24
|
traceparent_of,
|
|
24
25
|
with_session_parent,
|
|
@@ -317,10 +318,17 @@ class SpanHandle:
|
|
|
317
318
|
"""Handle around an OTel span. Use as a context manager to activate the span in the
|
|
318
319
|
current context, or keep it detached and call .end() manually."""
|
|
319
320
|
|
|
320
|
-
def __init__(
|
|
321
|
+
def __init__(
|
|
322
|
+
self,
|
|
323
|
+
span: Span,
|
|
324
|
+
client: Client | None,
|
|
325
|
+
state: _SpanState | None,
|
|
326
|
+
context: Context | None = None,
|
|
327
|
+
) -> None:
|
|
321
328
|
self.span = span
|
|
322
329
|
self._client = client
|
|
323
330
|
self._state = state
|
|
331
|
+
self._context = context
|
|
324
332
|
self._context_token: Token[Context] | None = None
|
|
325
333
|
self._ended = False
|
|
326
334
|
|
|
@@ -480,7 +488,9 @@ class SpanHandle:
|
|
|
480
488
|
|
|
481
489
|
def __enter__(self) -> SpanHandle:
|
|
482
490
|
if self._client is not None and self._state is not None:
|
|
483
|
-
self._context_token = otel_context.attach(
|
|
491
|
+
self._context_token = otel_context.attach(
|
|
492
|
+
trace.set_span_in_context(self.span, self._context)
|
|
493
|
+
)
|
|
484
494
|
return self
|
|
485
495
|
|
|
486
496
|
def __exit__(self, exc_type: Any, exc: Any, tb: Any) -> Literal[False]:
|
|
@@ -593,6 +603,11 @@ def start_span(
|
|
|
593
603
|
if isinstance(explicit_session_id, str)
|
|
594
604
|
else propagated.get(ATTR_SESSION_ID)
|
|
595
605
|
)
|
|
606
|
+
if not isinstance(session_id, str):
|
|
607
|
+
session_id = client._process_session_id # pyright: ignore[reportPrivateUsage]
|
|
608
|
+
if isinstance(session_id, str):
|
|
609
|
+
propagated[ATTR_SESSION_ID] = session_id
|
|
610
|
+
ctx = context_with_propagated_attributes(propagated, ctx)
|
|
596
611
|
ctx = with_session_parent(
|
|
597
612
|
ctx, session_id if isinstance(session_id, str) else None, client.config.api_key
|
|
598
613
|
)
|
|
@@ -605,7 +620,7 @@ def start_span(
|
|
|
605
620
|
start_time=_to_ns(start_time),
|
|
606
621
|
)
|
|
607
622
|
_SPAN_STATES[span] = state
|
|
608
|
-
handle = SpanHandle(span, client, state)
|
|
623
|
+
handle = SpanHandle(span, client, state, ctx)
|
|
609
624
|
# The processor stamps propagation on start; explicit fields still win.
|
|
610
625
|
if attrs:
|
|
611
626
|
span.set_attributes(attrs)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|