Built-in metrics in idunno.AtProto
This is a reference for metrics built-in for .NET, produced using the System.Diagnostics.Metrics API.
Tip
For more information about how to collect and report these metrics, see the .NET documentation
Collecting metrics.
During development you can use the dotnet-counters tool to
observe these metrics in real time. e.g.
dotnet-counters monitor --process-id <pid> --counters idunno.AtProto.AtProtoHttpClient
For production environments, you can use a variety of exporters to send these metrics to monitoring systems
like Aspire, Prometheus, Grafana, or Azure Monitor.
idunno.AtProto.AtProtoHttpClient
The idunno.AtProto.AtProtoHttpClient Meter reports measures from the idunno.AtProto.AtProtoHttpClient.
Metric: requests.total
| Name |
Instrument Type |
Unit |
Description |
requests.total |
Counter<long> |
{requests} |
Total number of requests made by an instance of idunno.AtProto.AtProtoHttpClient. |
Metric: responses.total
| Name |
Instrument Type |
Unit |
Description |
responses.total |
Counter<long> |
{responses} |
Total number of responses received by an instance of idunno.AtProto.AtProtoHttpClient. |
Metric: requests.total.successful
| Name |
Instrument Type |
Unit |
Description |
requests.total.successful |
Counter<long> |
{requests} |
Total number of successful requests made by an instance of idunno.AtProto.AtProtoHttpClient. |
Metric: requests.total.failure
| Name |
Instrument Type |
Unit |
Description |
requests.total.failure |
Counter<long> |
{requests} |
Total number of failed requests made by an instance of idunno.AtProto.AtProtoHttpClient. |
Metric: requests.total.dpop_retry
| Name |
Instrument Type |
Unit |
Description |
requests.total.dpop_retry |
Counter<long> |
{requests} |
Total number of requests retried due to DPoP nonce rotation or other DPoP related issues by an instance of the idunno.AtProto.AtProtoHttpClient. |
Metric: responses.total.deserialization_failure
| Name |
Instrument Type |
Unit |
Description |
responses.total.deserialization_failure |
Counter<long> |
{requests} |
Total number of responses that could not be deserialized from JSON by an instance of the idunno.AtProto.AtProtoHttpClient. |
Metric: request.duration
| Name |
Instrument Type |
Unit |
Description |
request.duration |
Histogram<double> |
s |
Duration of individual requests made by an instance of the idunno.AtProto.AtProtoHttpClient. |
Metric: requests.total.xrpc_request
| Name |
Instrument Type |
Unit |
Description |
requests.total.xrpc_request |
Counter<long> |
{requests} |
Total number of xRPC requests made by an instance of the idunno.AtProto.AtProtoHttpClient, tagged with the xrpc_endpoint. |
Metric: request.content_length
| Name |
Instrument Type |
Unit |
Description |
request.content_length |
Counter<long> |
By |
Sum of request body sizes reported by Content-Length, when that header is present. Tagged with server, xrpc_endpoint and http_method. This is the declared length, not a count of bytes actually transmitted. |
Metric: response.content_length
| Name |
Instrument Type |
Unit |
Description |
response.content_length |
Counter<long> |
By |
Sum of response body sizes reported by Content-Length, when that header is present. Tagged with server, xrpc_endpoint and http_method. This is the declared length, not a count of bytes actually consumed. |
The request.duration histogram is tagged with outcome, which is one of success, http_error, deserialization_error,
response_too_large, cancelled (the caller's cancellation token was cancelled), transport_error (including an HttpClient timeout) or exception. It also includes the server tag and, for XRPC calls,
the xrpc_endpoint tag.
Each DPoP retry records a separate attempt duration. The failed attempt is recorded before the retry starts, so its
http_error duration does not include time spent in the retry.
idunno.AtProto.Jetstream
The idunno.AtProto.Jetstream Meter reports measures from the idunno.AtProto.Jetstream.AtProtoJetstream client.
Metric: total.messages
| Name |
Instrument Type |
Unit |
Description |
total.messages |
Counter<long> |
{messages} |
Total number of messages received from the JetStream by an AtProtoJetstream instance. |
Metric: total.message_parsing_failures
| Name |
Instrument Type |
Unit |
Description |
total.message_parsing_failures |
Counter<long> |
{messages} |
Total number of messages that failed to parse after receipt by an AtProtoJetstream instance. |
Metric: total.message_decompression_failures
| Name |
Instrument Type |
Unit |
Description |
total.message_decompression_failures |
Counter<long> |
{messages} |
Total number of messages that failed to decompress after receipt by an AtProtoJetstream instance. |
Metric: total.events_parsed
| Name |
Instrument Type |
Unit |
Description |
total.events_parsed |
Counter<long> |
{events} |
Total number of events parsed from received messages by an AtProtoJetstream instance. |
Metric: total.unknown_events
| Name |
Instrument Type |
Unit |
Description |
total.unknown_events |
Counter<long> |
{events} |
Total number of events with unknown type received in messages by an AtProtoJetstream instance. |
Metric: total.faults
| Name |
Instrument Type |
Unit |
Description |
total.faults |
Counter<long> |
{faults} |
Total number of WebSocket faults that occurred by an AtProtoJetstream instance. |
Metric: total.connections_opened
| Name |
Instrument Type |
Unit |
Description |
total.connections_opened |
Counter<long> |
{connections} |
Total number of WebSocket connections to the JetStream opened by an AtProtoJetstream instance. |
Metric: total.connections_closed
| Name |
Instrument Type |
Unit |
Description |
total.connections_closed |
Counter<long> |
{connections} |
Total number of WebSocket connections to the JetStream closed by an AtProtoJetstream instance. |
Metric: total.connections_failed
| Name |
Instrument Type |
Unit |
Description |
total.connections_failed |
Counter<long> |
{connections} |
Total number of WebSocket connections to the JetStream that failed by an AtProtoJetstream instance. |
Metric: connections.active
| Name |
Instrument Type |
Unit |
Description |
connections.active |
UpDownCounter<long> |
{connections} |
Current number of open Jetstream connections, tagged with the configured server URI. |
Metric: connection.duration
| Name |
Instrument Type |
Unit |
Description |
connection.duration |
Histogram<double> |
s |
Duration of completed Jetstream connections, tagged with the configured server URI. |
Metric: message.interarrival.duration
| Name |
Instrument Type |
Unit |
Description |
message.interarrival.duration |
Histogram<double> |
s |
Time between successive messages received on a Jetstream connection, tagged with the configured server URI. A rising interval can indicate an idle or stalled stream; it measures message arrival, not application event processing. |
Archive snapshot and replay metrics
These counters use the same idunno.AtProto.Jetstream meter and a server tag containing only the
service origin (scheme, host and port), never credentials, paths or query parameters. They are separate
from the live WebSocket message counters above. Instrument names are prefixed with
idunno.atproto.jetstream. (for example, idunno.atproto.jetstream.total.archive_bytes).
| Name |
Instrument Type |
Unit |
Description |
total.archive_plans |
Counter<long> |
{plans} |
Successfully validated archive plan pages, including resumed pages. |
total.archive_blocks |
Counter<long> |
{blocks} |
Archive blocks decoded from either block or whole-segment downloads. |
total.archive_segments |
Counter<long> |
{segments} |
Planned segments completed, including those with no matching rows. |
total.archive_bytes |
Counter<long> |
By |
Archive response-body bytes read, including resumed reads and segment headers; excludes plan JSON and HTTP headers. |
total.archive_rate_limits |
Counter<long> |
{requests} |
HTTP 429 responses from archive planning and downloads (each retry is counted). |
total.archive_events |
Counter<long> |
{events} |
Archive events materialised and yielded after exact client-side filtering; at-least-once resume may count a row more than once. |
total.archive_filtered_events |
Counter<long> |
{events} |
Decoded archive rows rejected by exact client-side filtering, including planner false positives. |
total.replay_handoffs |
Counter<long> |
{handoffs} |
Attempts to connect to the live v2 stream after an archive snapshot or from a saved live checkpoint, including reconnects. |
idunno.AtProto.Firehose
The idunno.AtProto.Firehose Meter reports measures from the idunno.AtProto.Firehose.AtProtoFirehose client. Instrument names are prefixed with idunno.atproto.firehose. (for example, idunno.atproto.firehose.total.signing_key_cache_hits).
Register the meter with AddAtProtoFirehoseMetrics() from the OpenTelemetry.Metrics namespace:
builder.Services.AddOpenTelemetry()
.WithMetrics(metrics => metrics.AddAtProtoFirehoseMetrics());
For dependency-injection scenarios, supply the application's IMeterFactory through FirehoseOptions.MeterFactory.
| Name |
Instrument Type |
Unit |
Description |
total.messages |
Counter<long> |
{messages} |
Frames received from the firehose. |
total.events_parsed |
Counter<long> |
{events} |
Events parsed from the firehose. |
total.unknown_events |
Counter<long> |
{events} |
Frames with an unknown operation or message type. |
total.invalid_events |
Counter<long> |
{events} |
Events which failed validation and were yielded as FirehoseInvalidEvent. |
total.connections_opened |
Counter<long> |
{connections} |
Firehose connections opened. |
total.connections_closed |
Counter<long> |
{connections} |
Firehose connections closed. |
total.connections_failed |
Counter<long> |
{connections} |
Connection failures. |
total.reconnects |
Counter<long> |
{connections} |
Reconnection attempts. |
total.protocol_errors |
Counter<long> |
{errors} |
Connections dropped because of malformed frames or sequence violations. |
total.signing_key_cache_hits |
Counter<long> |
{lookups} |
Signing keys, or failures to resolve one, found in the signing key cache. Only recorded when VerifySignatures and CacheSigningKeys are both on. |
total.signing_key_cache_misses |
Counter<long> |
{lookups} |
Signing keys not found in the signing key cache, each of which resolved a DID document inline and delayed the stream. Only recorded when VerifySignatures and CacheSigningKeys are both on. |
total.signing_key_refreshes |
Counter<long> |
{lookups} |
Cached signing keys resolved again because a signature failed to verify against them, which may mean a key was rotated without an #identity event. |
connections.active |
UpDownCounter<long> |
{connections} |
Current number of open firehose connections, tagged with the server origin. |
connection.duration |
Histogram<double> |
s |
Duration of completed firehose connections, tagged with the server origin. |
message.interarrival.duration |
Histogram<double> |
s |
Time between successive frames received on a firehose connection, tagged with the server origin. A rising interval can indicate an idle or stalled stream; it measures frame arrival, not event processing. |
idunno.AtProto.DidHandleCache
The idunno.AtProto.DidHandleCache Meter reports measures from idunno.AtProto.DidHandleCache. Instrument names are prefixed with idunno.atproto.didhandlecache. (for example, idunno.atproto.didhandlecache.total.hits).
Register the meter with AddAtProtoDidHandleCacheMetrics(), and supply an IMeterFactory with DidHandleCacheOptions.MeterFactory.
Hits and misses are only recorded by ResolveHandleAsync(); TryGetCachedHandle() records nothing.
| Name |
Instrument Type |
Unit |
Description |
lookup.duration |
Histogram<double> |
s |
Duration of a ResolveHandleAsync() call, including time waiting for a shared resolution. Tagged with outcome: hit, miss, coalesced or rejected. |
pending_lookups |
UpDownCounter<long> |
{lookups} |
Current aggregate number of distinct lookups waiting for or performing a resolution across all DidHandleCache instances. |
| Name |
Instrument Type |
Unit |
Description |
total.hits |
Counter<long> |
{lookups} |
Handles, including handle.invalid, found in the cache. |
total.misses |
Counter<long> |
{lookups} |
Handles not found in the cache, each of which started a resolution. |
total.coalesced_lookups |
Counter<long> |
{lookups} |
Handles not found in the cache which joined a resolution already in progress for the same DID rather than starting another. |
total.rejected_lookups |
Counter<long> |
{lookups} |
Lookups which returned handle.invalid, without caching it, because Size lookups were already pending or a resolution slot did not become free within ResolutionTimeout. A sustained rise suggests a source sending more distinct DIDs than the cache can resolve. |
total.invalid_handles |
Counter<long> |
{resolutions} |
Resolutions which failed, timed out, or returned a handle which did not resolve back to the DID, and so returned handle.invalid. |
total.invalidations |
Counter<long> |
{invalidations} |
Calls to Invalidate(), including those made by the firehose or Jetstream for identity events. |
idunno.AtProto.Directory
The idunno.AtProto.Directory Meter reports measures from the idunno.DidPlcDirectory service.
Metric: idunno.atproto.directory.requests.total
| Name |
Instrument Type |
Unit |
Description |
requests.total |
Counter<long> |
{requests} |
Total number of requests made for DID documents. |
Metric: idunno.atproto.directory.requests.total.failed
| Name |
Instrument Type |
Unit |
Description |
requests.total.failed |
Counter<long> |
{requests} |
Total number of requests made for DID documents that failed. |
Metric: idunno.atproto.directory.requests.total.succeeded
| Name |
Instrument Type |
Unit |
Description |
requests.total.succeeded |
Counter<long> |
{requests} |
Total number of requests made for DID documents that succeeded. |
Metric: idunno.atproto.directory.request.duration
| Name |
Instrument Type |
Unit |
Description |
request.duration |
Histogram<double> |
s |
Duration of individual requests made for DID documents. |
idunno.Bluesky.AspNet.Authentication
The idunno.Bluesky.AspNet.Authentication Meter reports measures from the idunno.Bluesky.AspNet.Authentication components,
the authentication handler, the sign-in manager, the profile claims transformer and the identity stores.
All instrument names are prefixed with idunno.bluesky.aspnet.authentication., which is omitted from the tables below for brevity.
Metric: idunno.bluesky.aspnet.authentication.authentications.total
| Name |
Instrument Type |
Unit |
Description |
authentications.total |
Counter<long> |
{authentications} |
Total authentication attempts against a request carrying an authentication cookie. Requests with no authentication cookie are not counted, as they are not authentication attempts. |
Tagged with result, whose value is success when the request authenticated, or one of the following when it did not.
result |
Meaning |
unprotect_ticket_failed |
The authentication cookie could not be unprotected. |
did_missing_in_cookie |
The ticket carried no DID claim. |
invalid_did_in_cookie |
The ticket carried a DID claim which is not a valid DID. |
identity_missing_in_store |
The identity store no longer holds the identity the ticket points at. |
ticket_expired |
The ticket expired and could not be renewed. |
no_principal |
The ticket carried no principal. |
token_refresh_failed |
The access token needed refreshing and the refresh failed. |
identity_missing_after_refresh |
The identity vanished from the store while its token was being refreshed. |
token_refresh_wait_expired |
The request gave up waiting for another request to finish refreshing the token. |
request_cancelled |
The request was cancelled during authentication. |
failure |
A catch-all for any other failure, including results substituted by an application event handler. |
Metric: idunno.bluesky.aspnet.authentication.signins.total.successful
| Name |
Instrument Type |
Unit |
Description |
signins.total.successful |
Counter<long> |
{signins} |
Total successful sign-ins. |
Metric: idunno.bluesky.aspnet.authentication.signins.total.failure
| Name |
Instrument Type |
Unit |
Description |
signins.total.failure |
Counter<long> |
{signins} |
Total failed sign-ins. |
Tagged with reason, one of NoQueryString, NoCorrelationState or OAuth2StateFailure.
Metric: idunno.bluesky.aspnet.authentication.signouts.total
| Name |
Instrument Type |
Unit |
Description |
signouts.total |
Counter<long> |
{signouts} |
Total sign-outs. |
Metric: idunno.bluesky.aspnet.authentication.credentialrevocations.failures.total
| Name |
Instrument Type |
Unit |
Description |
credentialrevocations.failures.total |
Counter<long> |
{failures} |
Total failures revoking credentials at the PDS during sign-out. A non-zero value means tokens remain live at the PDS after the local session ended. |
Metric: idunno.bluesky.aspnet.authentication.correlationstate.rejections.total
| Name |
Instrument Type |
Unit |
Description |
correlationstate.rejections.total |
Counter<long> |
{rejections} |
Total OAuth correlation states rejected when processing a login callback. |
Tagged with reason.
reason |
Meaning |
missing_cookie |
The callback carried no correlation cookie, so it could not be tied to a login this application started. |
missing_state |
The callback did not carry exactly one OAuth state parameter, so it could not say which login in flight it belonged to. |
expired_cookie |
The correlation cookie was readable but had passed the expiry it was written with. |
unprotect_failed |
The correlation cookie could not be unprotected. This also counts against dataprotection.failures.total. |
malformed_cookie |
The correlation cookie was unprotected successfully but its contents could not be parsed. |
state_not_found |
The cookie was readable but the login state it referred to was not in the state cache, either because it expired or because it had already been consumed. |
Metric: idunno.bluesky.aspnet.authentication.tokenrefreshes.total
| Name |
Instrument Type |
Unit |
Description |
tokenrefreshes.total |
Counter<long> |
{refreshes} |
Total access tokens refreshed. |
Tagged with outcome, either self when the request performed the refresh itself, or concurrent when it picked up a token refreshed by another request.
Metric: idunno.bluesky.aspnet.authentication.tokenrefreshfailures.total
| Name |
Instrument Type |
Unit |
Description |
tokenrefreshfailures.total |
Counter<long> |
{refreshes} |
Total access token refresh failures. |
Metric: idunno.bluesky.aspnet.authentication.tokenrefreshwaits.total
| Name |
Instrument Type |
Unit |
Description |
tokenrefreshwaits.total |
Counter<long> |
{waits} |
Total waits during token refresh as another refresh is in progress. |
Tagged with reason, either refresh_in_progress for the ordinary case where the request queued behind a refresh it could see was running,
or lock_denied where the request tried to take the refresh lock and lost the race. A rising proportion of lock_denied indicates lock contention
rather than ordinary queueing.
Metric: idunno.bluesky.aspnet.authentication.tokenrefreshwaits.duration
| Name |
Instrument Type |
Unit |
Description |
tokenrefreshwaits.duration |
Histogram<double> |
s |
Duration of waits during token refresh as another refresh is in progress. |
Metric: idunno.bluesky.aspnet.authentication.identitystore.operations.duration
| Name |
Instrument Type |
Unit |
Description |
identitystore.operations.duration |
Histogram<double> |
s |
Duration of identity store operations. Every authenticated request reads from the identity store, so this is on the hot path for every request. |
Tagged with operation, one of add, get, remove or update.
Metric: idunno.bluesky.aspnet.authentication.identitystore.misses.total
| Name |
Instrument Type |
Unit |
Description |
identitystore.misses.total |
Counter<long> |
{misses} |
Total identities which were not in the identity store when a request needed them. Each miss signs a user out. |
Tagged with phase, either authentication when the identity was missing as the authentication cookie was read,
or token_refresh when it disappeared while its access token was being refreshed.
Metric: idunno.bluesky.aspnet.authentication.identitystore.writefailures.total
| Name |
Instrument Type |
Unit |
Description |
identitystore.writefailures.total |
Counter<long> |
{failures} |
Total identities which could not be written to the identity store. A write failure leaves the store without an identity the session depends on, so the user is signed out on their next request. |
Metric: idunno.bluesky.aspnet.authentication.identitystore.evictions.total
| Name |
Instrument Type |
Unit |
Description |
identitystore.evictions.total |
Counter<long> |
{evictions} |
Total identities evicted from the identity store because it reached its size limit. Only the EphemeralIdentityStore has a size limit. Evicting an identity silently signs that user out, so a non-zero value means the store is too small for the number of concurrent users. |
Note
The matching capacity warning is logged only once for the lifetime of the process, so this counter is the only way to see how often
the EphemeralIdentityStore is evicting identities.
Metric: idunno.bluesky.aspnet.authentication.profilecache.hits.total
| Name |
Instrument Type |
Unit |
Description |
profilecache.hits.total |
Counter<long> |
{hits} |
Total profile cache hits. |
Metric: idunno.bluesky.aspnet.authentication.profilecache.misses.total
| Name |
Instrument Type |
Unit |
Description |
profilecache.misses.total |
Counter<long> |
{misses} |
Total profile cache misses. A miss makes a call out to the PDS, so the hit rate against these two counters is a measure of how much request latency the profile cache is saving. |
Metric: idunno.bluesky.aspnet.authentication.handleverification.failures.total
| Name |
Instrument Type |
Unit |
Description |
handleverification.failures.total |
Counter<long> |
{failures} |
Total handles returned by a profile which did not resolve back to the DID they were returned for. A handle is only as trustworthy as the resolution which confirms it, so a failure means the handle was discarded rather than surfaced as a claim. |
Metric: idunno.bluesky.aspnet.authentication.dataprotection.failures.total
| Name |
Instrument Type |
Unit |
Description |
dataprotection.failures.total |
Counter<long> |
{failures} |
Total data protection failures. A sustained rise usually means key ring rotation or a key ring which is not shared across every instance of a multi-instance deployment. |
Tagged with source, either correlation_cookie or identity_store, identifying which payload could not be unprotected.
idunno.AtProto.OAuthCallback
Callback metric recording is best-effort. Exceptions thrown by metric listeners are logged as warnings without interrupting
HTTP responses, callback completion or server disposal.
The idunno.AtProto.OAuthCallback Meter reports measures from the local OAuth callback server. Register it with
AddAtProtoOAuthCallbackMetrics() from the OpenTelemetry.Metrics namespace:
builder.Services.AddOpenTelemetry()
.WithMetrics(metrics => metrics.AddAtProtoOAuthCallbackMetrics());
| Name |
Instrument Type |
Unit |
Description |
callbacks.total |
Counter<long> |
{callbacks} |
Callback completion outcomes, tagged with outcome: success when an authorization code was received, oauth_error when the provider returned an error or no code, timeout, cancelled, disposed or listener_error when the listener faults. |
callback.wait.duration |
Histogram<double> |
s |
Time from the first WaitForCallbackAsync() call until a callback, timeout, cancellation, disposal or listener failure. Tagged with the same outcome. Callbacks received and listener faults occurring before a wait begins have no duration measurement. |
requests.rejected.total |
Counter<long> |
{requests} |
Rejected requests, tagged with reason: bad_request for invalid callback requests and method_not_allowed for non-GET requests. |