Skip to main content

otel-metrics

Reusable OpenTelemetry library: simplified wrappers around OTel metric instruments, plus an OTLP tracing pipeline.

Install

pip install otel-metrics

Or from a built wheel:

pip install dist/otel_metrics-0.4.0-py3-none-any.whl

Usage

from enum import auto

from otel_metrics import MetricName, OtelConfig, setup_metrics

# Define your metric names
class MyMetrics(MetricName):
    REQUEST_COUNT = auto()
    REQUEST_DURATION_MS = auto()
    ACTIVE_CONNECTIONS = auto()

# Configure and initialize
config = OtelConfig(endpoint="http://localhost:4317", service_name="my-service")

# setup_metrics returns a context manager that flushes and shuts down on exit
with setup_metrics(config) as metrics:
    counter = metrics.counter(MyMetrics.REQUEST_COUNT, description="Total requests")
    timer = metrics.timer(MyMetrics.REQUEST_DURATION_MS, description="Request latency")

    counter.inc()

Calling setup_metrics again in the same process reuses the provider it already created. Pass set_global=False for isolated setups, or inject a provider with setup_metrics(config, meter_provider=provider) and the library will not shut it down.

Timing operations

with timer.time(attributes={"route": "/orders"}):
    do_work()

For explicit control, record a TimestampNS yourself:

from otel_metrics import TimestampNS

start = TimestampNS.now()
# ... do work ...
timer.record(start)

Up/down counters

Use an up/down counter for values that rise and fall, such as active connections:

active = metrics.up_down_counter(
    MyMetrics.ACTIVE_CONNECTIONS, description="Open connections"
)

active.inc()
active.dec()

inc adds and dec subtracts, both taking an optional amount and attributes. Negative amounts raise ValueError for counters and histograms.

Gauges

A gauge keeps one value per attribute set:

gauge = metrics.gauge(MyMetrics.QUEUE_DEPTH, description="Queued jobs")
gauge.set(12, attributes={"queue": "default"})
gauge.set(3, attributes={"queue": "billing"})
gauge.get(attributes={"queue": "default"})  # 12.0
gauge.remove(attributes={"queue": "billing"})

A gauge emits nothing until its first set.

Configuration

OtelConfig is a pydantic-settings model. Values resolve from constructor args or environment variables:

Field Env var Default
endpoint OTEL_EXPORTER_OTLP_ENDPOINT (required)
service_name OTEL_SERVICE_NAME (required)
protocol OTEL_EXPORTER_OTLP_PROTOCOL grpc
headers OTEL_EXPORTER_OTLP_HEADERS None
tags OTEL_RESOURCE_ATTRIBUTES None
metric_prefix OTEL_METRIC_PREFIX None (no prefix)

Supported protocols: grpc, http/protobuf.

metric_prefix is optional. When set, instrument names are prefixed with it (myapp.request_count). It is not derived from service_name automatically, because the OTel resource already carries service.name.

headers accepts either a mapping or the OTLP key=value,key2=value2 string form, and is forwarded to both the metrics and traces exporters. Values may be URL-encoded per the OTLP spec, but plain values are accepted too.

Header values are held as pydantic.SecretStr, because they usually carry an API key. Printing or dumping the config shows the header names but masks the values:

config = OtelConfig(
    endpoint="https://api.braintrust.dev/otel",
    service_name="my-service",
    headers="Authorization=Bearer sk-live-123",
)

print(config.model_dump_json())
# {"endpoint":"...","headers":{"authorization":"**********"}}

config.resolved_headers()
# {'authorization': 'Bearer sk-live-123'}

Call resolved_headers() when you need the real values. Reading config.headers gives SecretStr objects, not plain strings.

tags are optional OTel resource attributes attached to every metric and span. They accept a mapping or the OTLP key=value,key2=value2 string form (via OTEL_RESOURCE_ATTRIBUTES or the tags field), parsed like headers:

config = OtelConfig(
    endpoint="https://api.braintrust.dev/otel",
    service_name="my-service",
    tags="env=prod,region=us-east-1",
)

The explicit service_name always wins over a service.name tag.

Tracing

from otel_metrics import OtelConfig, setup_telemetry

config = OtelConfig(
    endpoint="https://api.braintrust.dev/otel",
    service_name="my-service",
    protocol="http/protobuf",
    headers={"Authorization": "Bearer <key>", "x-bt-parent": "project_id:<id>"},
)
tracer = setup_telemetry(config)

with tracer.start_as_current_span("my-operation"):
    ...

setup_telemetry sets the process-global tracer provider and reuses it on later calls. Pass set_global=False for isolated setups, or inject a provider with tracer_provider=. Use shutdown_telemetry() (or force_flush_telemetry()) for explicit lifecycle control. Use build_test_tracer() for a non-exporting tracer in unit tests.

Instrumentors

Pass any BaseInstrumentor as a trailing argument and it is attached to the provider built here -- no need to reach for the global one:

from opentelemetry.instrumentation.langchain import LangchainInstrumentor

setup_telemetry(config, LangchainInstrumentor())

Several are fine, and an existing list can be splatted:

setup_telemetry(config, LangchainInstrumentor(), RequestsInstrumentor())
setup_telemetry(config, *instrumentors)

Testing

Use build_test_metrics() to get a no-op Metrics instance for unit tests:

from otel_metrics import build_test_metrics

metrics = build_test_metrics()

Development

uv sync
uv run pytest
uv run ruff check .

Build

uv build

Produces a wheel and an sdist in dist/.

Metadata

Release files for otel-metrics 0.4.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for otel-metrics 0.4.0
File Size Uploaded Provenance
otel_metrics-0.4.0.tar.gz 15.5 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for otel-metrics 0.4.0
File Interpreter ABI Platform
otel_metrics-0.4.0-py3-none-any.whl Python 3 none any Details

Total release size: 27.1 kB

Release files / otel_metrics-0.4.0.tar.gz

Download URL otel_metrics-0.4.0.tar.gz
Size 15.5 kB
Tags Source
SHA-256 checksum
How to use checksums
82e70f353a91e9f8dc627f02c6e30054b1220a436ef43ab3cbbd021f6f509011
BLAKE2b-256 checksum
How to use checksums
6b9e8dd18d8e818d17ec5c42834291a9c21ef4f9464d6fc38a69c629d9cfc729
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 25, 2026.

Transparency log

Release files / otel_metrics-0.4.0-py3-none-any.whl

Download URL otel_metrics-0.4.0-py3-none-any.whl
Size 11.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
99bb3bb3ccbe67e61232b257b633bbe01ada3561743c3955965672f96c532010
BLAKE2b-256 checksum
How to use checksums
c8dbd48d2381c60e08b2eea0724a651e326c3fb5a81861aaf4bbf85d0a92dec6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 25, 2026.

Transparency log

Security for otel-metrics 0.4.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for otel_metrics-0.4.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 25, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page