telemetry-dev
telemetry.dev SDK for Python — OpenTelemetry-native GenAI tracing, logs, and metrics. Thin
ergonomic functions over OTel spans (gen_ai.* semantic conventions), exported as OTLP
protobuf to the telemetry.dev ingest.
Install
pip install telemetry-dev
# or
uv add telemetry-dev
Requires Python >= 3.10.
Quickstart
import telemetry_dev
from telemetry_dev import log, observe, propagate_attributes, start_span, update_current_span
telemetry_dev.init() # reads TELEMETRY_DEV_API_KEY from the environment
@observe # arguments -> input, return value -> output, errors captured + re-raised
def lookup_weather(city: str) -> dict:
return {"forecast": "sunny"}
with propagate_attributes(user_id="user_123", session_id="session_456"):
with start_span(
"chat gpt-4o",
type="generation",
model="gpt-4o",
provider="openai",
input=[{"role": "user", "content": "Plan a day trip"}],
):
log("calling the model")
update_current_span(
output=[{"role": "assistant", "content": "Here you go..."}],
usage={"input_tokens": 11, "output_tokens": 7},
finish_reason="stop",
)
lookup_weather("Kyoto")
telemetry_dev.flush()
The SDK fails open: without an API key every call is a silent no-op, and internal errors are
routed to the on_error hook / telemetry_dev logger — never raised into your code.
Environment variables
| Variable | Default | Purpose |
|---|---|---|
TELEMETRY_DEV_API_KEY |
— | Ingest key (td_live_...). Absent = SDK is a no-op. |
TELEMETRY_DEV_BASE_URL |
https://ingest.telemetry.dev |
Ingest base URL (trailing slashes stripped). |
TELEMETRY_DEV_ENVIRONMENT |
production |
Deployment environment label. |
OTEL_SERVICE_NAME |
unknown_service |
Service name on every trace. |
Explicit init() arguments take precedence over environment variables.
API reference
| Name | Description |
|---|---|
init(**options) -> Client |
Initialize the SDK (see options below). Calling again replaces the previous client. |
@observe / @observe(name=, type=, capture_input=, capture_output=, attributes=) |
Wrap a sync/async function (or generator) in a span. Arguments become input (param-name dict, self/cls dropped), the return value becomes output, exceptions are captured and re-raised. |
start_span(name, *, type="span", ...) -> SpanHandle |
Start a span. with activates it in the current context; without with it is a detached handle you must .end(). |
SpanHandle.update(**fields) / .end(**fields, end_time=) / .traceparent() |
Update attributes, end (accepts the full update field set), or read the W3C traceparent. |
update_current_span(**fields) |
Apply the update field set to the currently active span (no-op without one). |
propagate_attributes(*, user_id=, session_id=, metadata=) |
Context manager stamping user.id / gen_ai.conversation.id / td.metadata.* on every span and log record started inside (threads/asyncio included via contextvars). |
log(message, *, level="info", event_name=None, attributes=None) |
Emit an OTLP log record to /v1/logs, correlated with the current trace. Levels: debug/info/warn/error ("warning" is accepted as an alias of warn). |
get_traceparent() -> str | None |
W3C traceparent of the current context. |
flush(timeout_s=10.0) / shutdown(timeout_s=10.0) |
Force-flush / tear down traces + logs + metrics. Shutdown also runs atexit unless disable_atexit=True. |
TelemetrySpanProcessor / telemetry_dev.otel.create_telemetry_span_exporter |
Bring-your-own-OTel helpers (below). |
MaskContext, Usage, SpanHandle, Client, NOT_GIVEN |
Supporting types. |
Span types
type= maps to gen_ai.operation.name:
type |
operation | input / output attributes |
|---|---|---|
"span" (default) |
function |
gen_ai.input.messages / gen_ai.output.messages |
"generation" |
chat |
gen_ai.input.messages / gen_ai.output.messages |
"tool" |
execute_tool |
gen_ai.tool.call.arguments / gen_ai.tool.call.result |
"agent" |
invoke_agent |
gen_ai.input.messages / gen_ai.output.messages |
"embedding" |
embeddings |
gen_ai.input.messages / gen_ai.output.messages |
Span fields (start/update/end)
input, output, model, provider, system_instructions, response_model, response_id,
output_type, finish_reason, usage (dict with exactly input_tokens, output_tokens,
total_tokens, cache_read_input_tokens, cache_creation_input_tokens,
reasoning_output_tokens), cost_usd, temperature, top_p, top_k, max_tokens,
stop_sequences, seed, frequency_penalty, presence_penalty, time_to_first_chunk_ms,
tool_name, tool_call_id, tool_description, agent_name, agent_id, metadata
(→ td.metadata.*, this span only), attributes (raw escape hatch, merged last), error.
start_span additionally accepts parent (traceparent string, OTel Context, or
SpanContext), start_time, and per-call capture_input / capture_output overrides;
end() additionally accepts end_time.
init() options
| Option | Default | Purpose |
|---|---|---|
api_key, base_url, environment, service_name |
env vars | Connection + resource settings. |
enabled |
True |
False = hard kill switch (tests). |
register_global |
False |
Also register the tracer provider globally. Applies a default export filter (only telemetry_dev-scoped spans are exported); pass span_filter=lambda s: True to export everything. |
export_mode |
"batched" |
"immediate" exports synchronously per span/log (serverless). |
log_level |
"warn" |
SDK diagnostics level: debug/info/warn/error/silent. |
capture_input, capture_output |
True |
Global content-capture defaults. |
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. |
max_attribute_length |
65536 |
Per-content-attribute cap; truncated values get an ASCII ...[truncated] marker appended. |
span_filter |
None |
Export predicate Callable[[ReadableSpan], bool]. |
on_error |
None |
Receives every internal SDK error; the SDK never raises. |
disable_atexit |
False |
Skip the automatic atexit shutdown. |
timeout |
10.0 |
OTLP HTTP timeout in seconds. |
span_exporter, log_exporter, metric_reader |
None |
Test seams / offline mode; any of them enables the client without an API key. |
Auto-metrics
Ended spans automatically record two histograms (DELTA temporality, exported every 60s):
gen_ai.client.operation.duration(units) forchat,invoke_agent,embeddings,execute_toolgen_ai.client.token.usage(unit{token}, attributegen_ai.token.type=input|output) forchat,invoke_agent,embeddings
Plain spans (function) record no metrics. Quiet intervals produce zero metric requests.
Serverless
Use export_mode="immediate" and/or call telemetry_dev.flush() before the runtime freezes:
telemetry_dev.init(export_mode="immediate")
...
telemetry_dev.flush() # force-flush traces + logs + metrics
Bring your own OTel (telemetry_dev.otel)
If you already run an OpenTelemetry SDK, attach the telemetry.dev processor to your provider
instead of calling init():
from telemetry_dev.otel import TelemetrySpanProcessor
your_tracer_provider.add_span_processor(TelemetrySpanProcessor()) # reads TELEMETRY_DEV_API_KEY
If you need to wire your own span processor stack, create the telemetry.dev exporter directly:
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from telemetry_dev.otel import create_telemetry_span_exporter
span_exporter = create_telemetry_span_exporter()
your_tracer_provider.add_span_processor(BatchSpanProcessor(span_exporter))
TelemetrySpanProcessor remains available from the package root for compatibility:
from telemetry_dev import TelemetrySpanProcessor
| Option | Default | Purpose |
|---|---|---|
api_key |
TELEMETRY_DEV_API_KEY |
Ingest key. Absent with no span_exporter = inert no-op. |
base_url |
TELEMETRY_DEV_BASE_URL or https://ingest.telemetry.dev |
Ingest base URL; trailing slashes are stripped. |
export_mode |
"batched" |
"batched" or "immediate" span export. |
max_export_batch_size |
64 |
BatchSpanProcessor export batch size. |
schedule_delay_millis |
1000 |
BatchSpanProcessor schedule delay. |
max_queue_size |
2048 |
BatchSpanProcessor queue size. |
export_timeout_millis |
30000 |
BatchSpanProcessor export timeout. |
span_filter |
None |
Export predicate Callable[[ReadableSpan], bool]; failures export the span. |
metrics |
True |
Auto-record GenAI duration/token histograms for exported spans when an API key is available. |
service_name |
OTEL_SERVICE_NAME or unknown_service |
service.name on the metrics resource. |
environment |
TELEMETRY_DEV_ENVIRONMENT or production |
deployment.environment.name on the metrics resource. |
on_error |
None |
Receives internal processor errors; errors are never raised into your code. |
span_exporter |
None |
Advanced/test seam replacing the telemetry.dev OTLP trace exporter. |
Without an API key and without span_exporter, TelemetrySpanProcessor() is an inert no-op:
safe to attach unconditionally, with only a debug log. Auto-metrics are on by default for exported
GenAI spans, use the service_name / environment resource settings above, and are skipped
without an API key.
Limitations
register_global=Truecannot be undone onshutdown(): OpenTelemetry Python has no public API to unregister a globalTracerProvider, so a laterinit(register_global=True)in the same process cannot reclaim the global slot. Prefer the isolated default (or the BYO processor) for processes that re-initialize.
Development
Uses uv:
uv sync # install (writes uv.lock)
uv run pytest # tests
uv run ruff format . # format
uv run ruff check . # lint
uv run pyright # type-check
uv build # build wheel + sdist
examples/quickstart.py is runnable against a real ingest:
TELEMETRY_DEV_API_KEY=td_live_... uv run examples/quickstart.py.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file telemetry_dev-0.2.0.tar.gz.
File metadata
- Download URL: telemetry_dev-0.2.0.tar.gz
- Upload date:
- Size: 22.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.8.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b1ca317f3d2900ae97c219245c355aba014f102f6cbd5d46b099522229b43e59
|
|
| MD5 |
2e3e7acb977123c5da6be0cd8df21c15
|
|
| BLAKE2b-256 |
56fb39834ba6e2be1edf0917638c9a5a7a998e2bc77b26f411442b94726cdf43
|
File details
Details for the file telemetry_dev-0.2.0-py3-none-any.whl.
File metadata
- Download URL: telemetry_dev-0.2.0-py3-none-any.whl
- Upload date:
- Size: 28.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.8.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9b1a11866d355f439949c3a3be7c802e654f45c8a4c2382c7adf5b48f45fb673
|
|
| MD5 |
262a9c734c945df3784698546c3d5f9c
|
|
| BLAKE2b-256 |
8fdc369eebf1b269d2af0d174cb61fdc60a27e867f215adb12fe19336734b370
|