otel_graphql 0.1.0-beta.1
otel_graphql: ^0.1.0-beta.1 copied to clipboard
OpenTelemetry instrumentation for the `gql_link` chain used by `package:graphql` and `package:graphql_flutter`. A Link that emits one span per GraphQL operation with `graphql.*` semconv attributes.
dartastic_graphql_otel #
OpenTelemetry instrumentation for the gql_link chain used by
package:graphql and
package:graphql_flutter.
Built on the
Dartastic OpenTelemetry SDK.
Insert one Link at the head of your chain and every GraphQL
operation emits a span with the operation type, name, and (opt-in)
the printed document.
final link = Link.from([
OTelGraphqlLink(),
HttpLink('https://api.example.com/graphql'),
]);
final client = GraphQLClient(link: link, cache: GraphQLCache());
Works for both pure-Dart graphql apps (servers, CLIs) and
Flutter apps that use graphql_flutter. Flutter apps may prefer
dartastic_graphql_flutter_otel (the matching overlay) to wire
graphql_flutter in one step.
Why #
GraphQL operations are coarser than HTTP requests — one POST /graphql can be any of hundreds of operations the front end
issues. If you only have HTTP spans, every span has the same name
and you can't compare latency across operations. This Link gives
you one span per operation name, so a Tempo span-by-name view
slices cleanly by GraphQL endpoint.
The integration is opt-in: the OTel SDK does not depend on
gql_link or graphql. Add this package only when you want it.
Span shape #
| Attribute | Source | When set |
|---|---|---|
graphql.operation.type |
query / mutation / subscription |
when the document specifies one |
graphql.operation.name |
Operation.operationName |
when present (anonymous operations don't set it) |
graphql.document |
printNode(operation.document) (clipped) |
only when recordDocument: true |
- Span name:
<type> <name>(e.g.query GetUser), falling back to the operation type alone for anonymous operations, orgraphqlif even that is unknown. - Span kind: default
INTERNAL. The actual network call is usually wrapped by the underlying HTTP / WebSocket Link's own instrumentation — that produces aCLIENTspan as a child of this one. - Span status:
Errorif the response carries GraphQL errors (Response.errors) or the underlying stream throws, otherwise unset.
Lifecycle #
The Link emits a span when an operation enters, and ends it when the response stream closes:
- Query / mutation: single-emission stream — span ends right after the response is yielded.
- Subscription: multi-emission stream — span ends when the consumer cancels or the server closes the stream.
GraphQL errors inside a response (partial failures) flip the span status to Error on first occurrence but the stream keeps flowing — Tempo still sees the rest of the data path.
Configuration #
| Constructor arg | Default | Effect |
|---|---|---|
tracer |
OTel.tracerProvider().getTracer('dartastic_graphql_otel') |
The tracer that emits the spans. |
recordDocument |
false |
Capture the pretty-printed GraphQL document as graphql.document. Off because documents can leak schema details and variable shapes. |
documentMaxLength |
1024 |
Cap on graphql.document when recording is enabled. |
Caveats #
- Place the OTel link first in the chain so its span encloses whatever auth / retry / HTTP links do downstream.
- Variable values are never captured — only the document text (and only when explicitly opted in). This is intentional: variables are the most reliable place for user data to live in a GraphQL request.
- The link calls
OTel.tracerProvider().getTracer(...)in its constructor —OTel.initialize()must have run first.
License #
Apache 2.0 — see LICENSE.