OpenTelemetry tracing for the message-store package via store interception
Project description
message-store-open-telemetry
OpenTelemetry distributed tracing for the message-store
package (NATS JetStream event sourcing).
It wraps a message store and layers tracing on top of its public interface — the same interception pattern used in the JavaScript version. No tracing code lives in the message store core; all OpenTelemetry concerns are contained here.
- Publish → a
PRODUCERspan is started and W3C trace context is injected into the NATS message headers (not the event body, so application schemas stay clean). - Consume → each handler runs inside a
CONSUMERspan whose parent is extracted from those headers, joining the same trace across the service hop. A backdatedqueue_waitspan captures how long the message sat in the stream before delivery. ensure_stream,fetchandwait_forpass straight through, untouched.
Installation
pip install message-store-open-telemetry
# or: uv add message-store-open-telemetry
Depends on message-store>=0.4.0 (the version that exposes _raw_jetstream_message and the publish
headers parameter this package relies on) and opentelemetry-api. nats-py arrives transitively
through message-store.
Usage
Wrap the store immediately after building it:
import nats
from message_store import MessageStore
from message_store_open_telemetry import get_message_store_with_open_telemetry
nats_connection = await nats.connect("nats://127.0.0.1:4222")
message_store = get_message_store_with_open_telemetry(
MessageStore(
nats_connection,
prefix="my-service",
should_create_missing_streams=True,
)
)
# Use `message_store` exactly as before — publishing and subscriptions are now traced.
The wrapper exposes the same surface as MessageStore. Type code that accepts either the concrete
store or a wrapped one against MessageStoreProtocol (exported from message_store):
from message_store import MessageStoreProtocol
def register(store: MessageStoreProtocol) -> None:
...
Enabling the SDK
This library only uses the OpenTelemetry API. Spans are emitted only once the host process registers an OpenTelemetry SDK and tracer provider. With no SDK registered, every API call is a cheap no-op, so the wrapper is safe to apply in services that are not exporting traces yet.
A minimal SDK setup (configure the exporter for your collector):
from opentelemetry import trace
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
provider = TracerProvider(resource=Resource.create({"service.name": "my-service"}))
provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter()))
trace.set_tracer_provider(provider)
Emitted spans and attributes
Publish — nats publish <subject> (PRODUCER)
| Attribute | Source |
|---|---|
messaging.system |
"nats" |
messaging.operation |
"publish" |
messaging.destination.name |
published subject |
messaging.message.body.type |
message.type |
messaging.nats.stream |
from the returned PubAck |
messaging.message.seq |
from the returned PubAck |
Consume — nats process <subject> (CONSUMER)
Parented to (and linked to) the producer span when trace headers are present.
| Attribute | Source |
|---|---|
messaging.system |
"nats" |
messaging.operation |
"process" |
messaging.destination.name |
message subject |
messaging.message.seq |
message sequence |
messaging.message.body.type |
message.type |
messaging.nats.consumer |
consumer (durable) name |
messaging.nats.stream |
raw JetStream message |
messaging.nats.redelivery_count |
raw JetStream message |
queue_wait — a child span backdated to the moment JetStream stored the message and ended at
delivery, so its duration is the stream dwell time (tagged with messaging.nats.dwell_ms and
messaging.nats.redelivery_count).
Handler errors are recorded on the consumer span and re-raised, so the message store core keeps full ownership of
ack/nak/term.
How context propagates
The publish seam injects the active context into a NATS headers dict via inject; the consume seam
reads them back via extract. Whatever propagator your SDK installs (W3C tracecontext by default)
is honoured — no propagator is forced here. Trace context never touches the persisted event payload.
Exports
get_message_store_with_open_telemetry(store: MessageStoreProtocol) -> MessageStoreProtocoltracer— the library'sTracerinstance, exported for advanced use.
Project details
Release history Release notifications | RSS feed
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 message_store_open_telemetry-0.1.0.tar.gz.
File metadata
- Download URL: message_store_open_telemetry-0.1.0.tar.gz
- Upload date:
- Size: 5.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: Hatch/1.17.0 {"ci":null,"cpu":"x86_64","distro":{"id":"jammy","libc":{"lib":"glibc","version":"2.35"},"name":"Ubuntu","version":"22.04"},"implementation":{"name":"CPython","version":"3.11.15"},"installer":{"name":"hatch","version":"1.17.0"},"openssl_version":"OpenSSL 3.5.5 27 Jan 2026","python":"3.11.15","system":{"name":"Linux","release":"6.8.0-124-generic"}} HTTPX2/2.5.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7e47763b7aa695a84aa82713847cc117b569fd7f52fe5a388f51fa150bae9dbd
|
|
| MD5 |
b098036b863ab0b8b01b9be795ca6da9
|
|
| BLAKE2b-256 |
f0aafa41af348f499c322e5846c39ce850fb50cd79bc38b83d4badf32940988c
|
File details
Details for the file message_store_open_telemetry-0.1.0-py3-none-any.whl.
File metadata
- Download URL: message_store_open_telemetry-0.1.0-py3-none-any.whl
- Upload date:
- Size: 6.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: Hatch/1.17.0 {"ci":null,"cpu":"x86_64","distro":{"id":"jammy","libc":{"lib":"glibc","version":"2.35"},"name":"Ubuntu","version":"22.04"},"implementation":{"name":"CPython","version":"3.11.15"},"installer":{"name":"hatch","version":"1.17.0"},"openssl_version":"OpenSSL 3.5.5 27 Jan 2026","python":"3.11.15","system":{"name":"Linux","release":"6.8.0-124-generic"}} HTTPX2/2.5.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ae317def4ceda2f524a500713e2607ab9cb1ff84128a7ee7c56749ca3977d47b
|
|
| MD5 |
227a90adf3b086cd0a3ecc0e041db458
|
|
| BLAKE2b-256 |
27b9d1333abf2eff6ad36fc6b7a39eb861bb9588af8bb5c231db1283f8a18714
|