rpc_dart_opentelemetry 0.3.6
rpc_dart_opentelemetry: ^0.3.6 copied to clipboard
OpenTelemetry tracing, metrics, and logging integration for rpc_dart.
0.3.6 #
Observability (best-effort error sites):
OtelRpcInterceptorBase._finish/_finishWithError: the twometrics?.recordCall(...)calls were wrapped in emptycatch (_) {}, so a misconfigured meter failed silently. The exception is still swallowed (telemetry must never break the wrapped RPC call), but it is now surfaced on the active span via a new_recordMetricsFailurehelper that adds arpc.metrics.record_failedspan event (log.level=debug,exception.message,exception.stacktrace). No logger is injected into this class, so the span — the diagnostic facility it owns — is the cleanest sink.LogControllerOtelOutput(dispose,_sweepExpired,_evictOverflow): the three best-effortspan.end()calls keep their emptycatch (_) {}but now carry explicit comments documenting WHY the failure is intentionally not logged: this class IS aLogOutput, so routing an OTel export failure into a logger would re-enter the sameLogControllerthat feeds this output and recurse. Each comment also notes the failure must not abort the surrounding flush/sweep/eviction.- No control-flow change; observability only.
0.3.5 #
Fixes (audit):
RpcOtelMetrics: a single instance is shared by both the server interceptor (OtelRpcInterceptor) and the client interceptor (OtelRpcClientInterceptor) through the commonOtelRpcInterceptorBase.recordCallflow. The instrument names were hardcoded torpc.server.requests/rpc.server.duration, so client-side calls were recorded under the server namespace — a violation of the OTel RPC semantic conventions (client metrics must berpc.client.*).RpcOtelMetricsnow creates both instrument sets (rpc.server.requests/rpc.server.durationandrpc.client.requests/rpc.client.duration);recordCalltakes a newRpcMetricSide sideparameter (defaultRpcMetricSide.server, backward compatible) and routes the measurement to the matching instruments. Each interceptor reports its own side via a newOtelRpcInterceptorBase.metricSidegetter (server ->RpcMetricSide.server, client ->RpcMetricSide.client), mirroring theSpanKindit already uses. Added a regression test (a6_client_server_metric_namespace_test.dart) asserting a server call lands only inrpc.server.*, a client call only inrpc.client.*, and a shared instance keeps the two sides separate.
0.3.4 #
Refactor (no behavior change):
OtelRpcInterceptor(server) andOtelRpcClientInterceptor(client) were ~95% identical. Extracted the shared flow into a newOtelRpcInterceptorBase(lib/src/interceptor/otel_rpc_interceptor_base.dart): it holds thetracer/metricsfields, the fourintercept*methods, and the span finishing/stream-wrapping helpers (_finish,_finishWithError,_wrapWithSpan). Each subclass now implements onlystartSpan— the single point of difference (server extracts the W3C parent context, usesSpanKind.server, and addsrpc.trace_id; client injects the context, usesSpanKind.client, and callsupdateContext). Public API, const constructors, and barrel exports are unchanged. Future fixes to the shared flow now apply once instead of twice.
0.3.3 #
Fixes (audit):
LogControllerOtelOutput: the defaultspanTtlwas30ms, which made the periodic sweep force-end ANY log-span still doing work after 30ms — truncating normal traces (exported with a ~30ms duration, missing the final status/attributes/error, and silently dropping the real end record). The TTL is now a leak-guard, not a duration cap: the default is5 minutes(sweep interval30s).maxOpenSpansLRU eviction remains the primary bound. Both stay configurable. Added a regression test asserting a span legitimately open longer than the sweep interval is NOT force-ended while live and carries its real duration on its real end record.- Stream-wrapping span end (
_wrapWithSpanin both server and client interceptors): the source is listened withcancelOnError: false, so anonErroris NOT terminal — a server/bidi stream may emit a non-fatal item error and then keep emitting or complete normally. Previously the span was ended (and marked errored) on the FIRST error, dropping all later messages and the real completion. The span now ends on stream TERMINATION (onDone/onCancel) exactly once; each error is recorded on the span as an exception event as it arrives, and the last error sets the final error status. Added a regression test.
0.3.2 #
Web / dart2js correctness:
- Verified the full web-facing surface (both interceptors,
RpcOtelMetrics,RpcOtelPropagator,LogControllerOtelOutput) compiles to JS and runs on the real web target: the audit suite passes under-p chrome. Nodart:iois imported; stream wrapping uses an explicitStreamControllerwith anonCancelhook (noasync*generator), so subscription cancel propagates cleanly on dart2js. - Added
test/web_smoke_test.dart— a dart2js smoke that exercises the propagator inject/extract round-trip, the status-name/error-code tables, and theInt64nanosecond timestamp conversion. It runs green onvm,chrome, andnode. - The audit suite is now tagged
@TestOn('vm || chrome'): it builds a real opentelemetry SDK tracer whoseIdGeneratorcallsRandom.secure(), which is unavailable under thenodetest platform (an environment limitation, not a package bug). This keepsdart test -p node test/green. - Documented the dart2js timestamp caveat in
LogControllerOtelOutput._toInt64:DateTime.microsecondsSinceEpochhas only millisecond resolution on the web, so span timestamps round to the nearest millisecond there. Values remain valid nanosecond epochs with preserved ordering — only precision is reduced.
Metrics (#6 — OTel histogram):
- Still blocked upstream. Checked
opentelemetryup to the latest published version (0.18.11);Meterexposes onlycreateCounter(no Histogram / UpDownCounter).rpc.server.durationtherefore remains a sum counter; theTODO(#6)now records the version surveyed.
0.3.1 #
Fixes (audit):
- The RPC span is now installed as the ambient OTel
Context.currentfor the duration of the handler, so spans and log-spans created inside the handler are correctly parented under the RPC span (server and client interceptors). rpc.grpc.status_codeis now emitted as the numeric semconv integer (0..16) on both spans and metrics; the human-readable name moved to the non-semconv keyrpc.grpc.status.LogControllerOtelOutputno longer leaks un-ended spans: the open-span map is now bounded with LRU + TTL eviction (configurablemaxOpenSpans,spanTtl,sweepInterval).- Measured call duration is now recorded via the
rpc.server.durationcounter (was collected but discarded). TODO: switch to a histogram once the OTel API exposes one. - Added a test suite (the package previously shipped with none).
0.3.0 #
LogControllerOtelOutput— aLogOutputthat mirrors everyLogControllerspan/event into OpenTelemetry.LogSpanStartopens an OTel span with the same timestamp; nestedLogScope.startSpan(...)calls become child OTel spans (relies on the newLogSpanStart.parentSpanIdfield inrpc_dartcore).LogEvents with aspanIdbecomespan.addEvent(...); standalone events emit a single-shot span. ProviderootContextProvider: () => Context.currentto nest log-spans under the active RPC span.- Note: depends on
rpc_dart≥ the release that shipsLogSpanStart.parentSpanId/traceId. Bridge silently degrades to flat spans on older versions (only direct children of the root context). - Metrics now follow OpenTelemetry RPC semantic conventions.
- New: single counter
rpc.server.requestswithrpc.system,rpc.service,rpc.method, andrpc.grpc.status_code(uppercase canonical name) attributes — both success and error increments share the same counter. - Removed:
rpc_dart.calls.totalandrpc_dart.errors.total. RpcOtelMetrics.recordCallnow requiresstatusCode:andduration:.recordErrorwas removed (status code label distinguishes the outcome).
- New: single counter
OtelRpcInterceptornow setsrpc.grpc.status_codeas a span attribute and derives the code fromRpcStatusExceptionwhen an error is thrown.- New
OtelRpcClientInterceptor— client-side counterpart that creates aSpanKind.clientspan and injects W3Ctraceparent/tracestateinto the outgoingRpcContext. Cross-service traces now stitch automatically. - New
rpcGrpcStatusName(code)helper exposing the canonical gRPC status names used for therpc.grpc.status_codelabel.
0.2.0 #
- Updated to
rpc_dart: ^3.0.0. OtelRpcInterceptor: adapted to new resilience interceptor interfaces in core.
0.1.0 #
- Initial release:
OtelRpcInterceptorfor OpenTelemetry spans with W3C trace context propagation. RpcOtelMetricsfor RPC call metrics via OpenTelemetry.