Add OTel metrics support#1154
Conversation
|
Claude finished @mtmk's task in 1m 51s —— View job PR Review: Add OTel Meter and metric instruments
The change is clean and minimal. Instrument names and types all match the OTel messaging metrics spec. Two items worth tracking:
No correctness or security issues found. Instrument types ( |
ServerInfo.Host/Port reflects the server's bind address (often 0.0.0.0), not the address the client dialled. Per OTel semconv these tags should be from the client's perspective.
The increment at PublishAsync entry fires before ConnectAsync completes on a cold publish, so the first measurement is emitted with an empty tag prefix (server.address/port unset). Move it next to sent.bytes which is incremented post-serialization with the full tag set.
The previous implementation attached the handler after ConnectAsync, so the async event-channel delivery of the initial ConnectionOpened could satisfy the awaitable before the reconnect ran. Attach before connect and count to two.
|
@claude review |
|
@claude review changes in this PR |
|
Claude finished @mtmk's task in 5m 27s —— View job PR Review: Add OTel metrics support
|
OTel messaging semconv recommends specific bucket boundaries for messaging.client.operation.duration. Without them the SDK falls back to HTTP-style defaults that have no resolution for sub-millisecond NATS ops and skew published semconv dashboards. InstrumentAdvice<T> requires System.Diagnostics.DiagnosticSource 9.0+, so bump the package floor on netstandard2.0/2.1/net8.0. net10.0 keeps using the in-box assembly. 9.0.1 is the minimum because 9.0.0 trims the ActivitySource constructor under NativeAOT.
This reverts commit 7e8f2e2.
Behavior changes: * Balance ActiveSubscriptions across listener-attach and ctor cancellation-token races so the gauge cannot drift negative or leak +1 per sub. * Record operation.duration with error.type on synchronous publish throws (e.g. NatsPayloadTooLargeException, serializer faults) when no ActivityListener is attached. * Count consume metrics only after the message is handed off to the subscription channel, matching OTel "delivered to the application" semantics. Late replies that arrive after a Direct mode timeout no longer inflate the counters. 503 NoResponders sentinels are excluded on the Direct path for parity with SharedInbox. * Move Reconnects.Add out from under _gate so a slow MeterListener callback cannot stall the reconnect transition. * Swallow exceptions inside RecordOperationDuration so a buggy listener cannot replace an in-flight messaging exception in the catch/finally wrappers around publish, request, and subscribe. Refactor: * Build the metric TagList once per hot-path site and reuse it across the published/sent and consumed/received counter pairs. * Publish _metricTagsPrefix before _writableServerInfo so any reader observing the new ServerInfo is guaranteed to see the matching prefix. Docs and comments: * Clarify active_subscriptions semantics (includes SharedInbox muxer registrations created per RequestAsync), published.messages "attempted" semantics, and the rationale for the concrete-type check in BuildMetricTags.

Adds OpenTelemetry metrics on the existing
NATS.Netmeter alongsidethe activity source. Opt-in via
AddNatsClientInstrumentation()on aMeterProviderBuilder; no overhead without a listener.Targets
release/3.0. Refs #1153. Remaining: JetStream ack duration (#1158).