otel_riverpod 0.1.0-beta.1
otel_riverpod: ^0.1.0-beta.1 copied to clipboard
OpenTelemetry instrumentation for `package:riverpod`. Adds a ProviderObserver that emits short-lived spans for provider add / update / fail / dispose events, with semconv-style attributes so you can s [...]
otel_riverpod #
OpenTelemetry instrumentation for package:riverpod,
built on the Dartastic OpenTelemetry SDK.
Add one ProviderObserver and every provider event gets a short span
on the active trace:
provider.added:<name>when a provider initializes.provider.updated:<name>on each state change.provider.failed:<name>withrecordException+Errorstatus when a provider throws.provider.disposed:<name>when an auto-dispose provider unloads.
Each span carries the provider's name, runtime type, family argument (if any), auto-dispose flag, and the value's runtime type. Together they give you a Tempo timeline of your state graph that joins the caller's existing trace, so a UI gesture → mutation → provider update → HTTP call shows up as a single waterfall.
Why #
Riverpod's ProviderObserver is exactly the hook OTel was designed
to plug into, but writing one yourself means picking attribute keys,
deciding what to do on update vs. add vs. fail, and handling the
auto-dispose + family + mutation cases. This package is that
observer, written against a typed semconv enum once.
The integration is opt-in: the OTel SDK does not depend on
riverpod. Add this package only when you want it.
For Flutter apps using flutter_riverpod, see the companion package
otel_flutter_riverpod (same observer, exported via
ProviderScope).
Usage #
Pure Dart:
import 'package:dartastic_opentelemetry/dartastic_opentelemetry.dart';
import 'package:otel_riverpod/otel_riverpod.dart';
import 'package:riverpod/riverpod.dart';
Future<void> main() async {
await OTel.initialize(serviceName: 'my-app');
final container = ProviderContainer(
observers: [OTelRiverpodObserver()],
);
// Wrap your business logic in a span so the observer's child spans
// attach to *something* rather than living on standalone traces.
await OTel.tracer().startActiveSpanAsync<void>(
name: 'handle-request',
fn: (_) async {
container.read(myProvider);
},
);
container.dispose();
await OTel.shutdown();
}
Flutter (via flutter_riverpod):
runApp(
ProviderScope(
observers: [OTelRiverpodObserver()],
child: const MyApp(),
),
);
Configuration #
| Constructor arg | Default | Effect |
|---|---|---|
tracer |
OTel.tracerProvider().getTracer('otel_riverpod') |
The tracer that emits the spans. |
recordValues |
false |
When true, records toString() of provider values on added / updated events. Off by default because state often carries user data. |
recordUpdates |
true |
When false, suppresses didUpdateProvider spans entirely — useful for chatty providers in production. |
valueAttributeMaxLength |
256 |
Cap on any toString()-derived attribute. Longer strings get clipped with …. |
Span shape #
| Attribute | Source | When set |
|---|---|---|
riverpod.provider.name |
provider.name or runtime type |
always |
riverpod.provider.type |
provider.runtimeType (Provider, NotifierProvider, …) |
always |
riverpod.provider.auto_dispose |
provider.isAutoDispose |
always |
riverpod.event |
added / updated / failed / disposed |
always |
riverpod.provider.family |
provider.from?.name |
family providers |
riverpod.provider.argument |
provider.argument.toString() (clipped) |
family providers |
riverpod.value.type |
value.runtimeType |
when value is non-null |
riverpod.value |
value.toString() (clipped) |
only when recordValues: true |
riverpod.previous_value |
previousValue.toString() (clipped) |
updated events + recordValues: true |
riverpod.mutation |
ProviderObserverContext.mutation.toString() |
when triggered by a @mutation |
- Span name is
provider.<event>:<name>, e.g.provider.updated:counter. - Span kind is the default (
INTERNAL). - Span status is
Erroronly onproviderDidFail; everything else stays unset.
Caveats #
- The observer calls
OTel.tracerProvider().getTracer(...)in its constructor —OTel.initialize()must have run first. providerDidFailruns for every error a provider sees, including errors propagated throughFuture/Stream. Expect more than one failed span if you re-listen to a still-broken provider.- If you set
recordValues: true, make sure your provider state'stoString()is safe to log (no credentials, no PII). The default is off precisely so you have to opt in.
License #
Apache 2.0 — see LICENSE.