Skip to content

Metrics

Mark Lauter edited this page Jun 25, 2026 · 3 revisions

Metrics

AddPool wires up IPoolMetrics, implemented by DefaultPoolMetrics over System.Diagnostics.Metrics — the API OpenTelemetry consumes directly. Every pool publishes under one stable meter and carries its identity as a pool.name tag, so a single subscription captures every pool in the process. This page lists the instruments and how to collect them.

Upgrading from 7.0? The meter, instrument names, and tags changed in 7.1. See UPGRADING for the before/after mapping and migration steps.

The meter

Every pool publishes under PoolMeter.Name, the constant "MSL.Pool". Pool identity rides as a pool.name tag on every measurement rather than living in the instrument name, so all pools — named or not — aggregate under the one meter and you slice by pool as a dimension. The meter is stamped with the library version so consumers can attribute instruments to a release.

Instruments

Every measurement carries a pool.name tag. A bounded pool tags it "{typeof(TPoolItem).Name}.Pool"; an unbounded pool tags it "{typeof(TPoolItem).Name}.UnboundedPool", so the two report as distinct dimensions under the one meter. The two exception counters carry an additional error.type tag holding the fully qualified exception type.

Counters:

  • pool.lease.exceptions — failed leases, tagged error.type.
  • pool.preparation.exceptions — failed preparations, tagged error.type.
  • pool.leases.leaked — leases garbage-collected without being returned. See Avoiding footguns.

Histograms, in seconds (the OpenTelemetry convention), with bucket boundaries tuned for sub-millisecond-to-seconds pool latencies:

  • pool.lease.wait.duration — time spent acquiring an item, excluding preparation.
  • pool.item.preparation.duration — time spent preparing an item.

Observable up/down counters of live pool state:

  • pool.items.allocated — items the pool owns, leased plus idle.
  • pool.items.available — idle items.
  • pool.leases.active — items currently leased.
  • pool.leases.queued — lease requests waiting. Always 0 for an unbounded pool, whose leases never wait.

Observable gauge:

  • pool.utilization — active leases over allocated items.

Collect the metrics

Subscribe any System.Diagnostics.Metrics listener — OpenTelemetry, dotnet-counters, or a custom exporter — to the one meter:

services.AddOpenTelemetry()
    .WithMetrics(metrics => metrics
        .AddMeter(PoolMeter.Name)
        .AddPrometheusExporter());

That single AddMeter captures every pool in the process. Distinguish instances by the pool.name tag — ReadPool and WritePool over DbConnection both report under MSL.Pool, separated by their tag values rather than by separate meters.

The library takes no dependency on the OpenTelemetry SDK; it exposes the meter through System.Diagnostics.Metrics and leaves the choice of exporter to the host.

Replace the default implementation

To replace the default metrics, implement IPoolMetrics and register it before AddPool resolves its own:

services.AddSingleton<IPoolMetrics, MyPoolMetrics>();

IPoolMetrics declares two Record* methods for the histograms, two Record*Exception methods for the counters, and five Register*Observer methods that each return an IDisposable. The pool holds those handles for its lifetime and disposes them on pool disposal, which severs the observation callback so a disposed pool stops reporting.

Clone this wiki locally