# ADR-0024: Expose OTel metrics with the (prerelease) Prometheus AspNetCore exporter - **Status:** Accepted - **Date:** 2026-07-24 - **Deciders:** Respellion engineering - **Slice:** S-16c (#124), last of the S-16 (#17) split ## Context ADR-0023 already fixed the shape of metrics collection: **Prometheus scrapes each service's `/metrics`** (pull, no collector). S-16c implements it. That needs a package that turns the OpenTelemetry `MeterProvider` into a Prometheus scrape endpoint inside ASP.NET Core. The canonical one is `OpenTelemetry.Exporter.Prometheus.AspNetCore` (`AddPrometheusExporter()` + `app.MapPrometheusScrapingEndpoint()`). The catch: that exporter has **never had a stable release** — the whole OTel .NET Prometheus exporter line is versioned `-beta` (we pin `1.17.0-beta.1`, matched to the `1.17.0` core we already use). Adding it is a new dependency (CLAUDE.md §14), and taking a prerelease package into all five services is the decision worth recording. ## Decision **Add `OpenTelemetry.Exporter.Prometheus.AspNetCore` `1.17.0-beta.1` to the five .NET services and expose `/metrics` with it.** - What it gives us: the OTel-native pull endpoint, so the meters we already register for tracing-adjacent instrumentation surface as Prometheus text with zero extra plumbing. - What we'd write to replace it: a hand-rolled `IMetricsListener`/`MeterListener` that formats Prometheus exposition text — real work, and a reimplementation of a widely-used library for no gain. - Risk it adds: a prerelease API that can shift between betas. Contained: it is only wired in `Program.cs` (two calls per service, excluded from mutation), the version is pinned, and `verify-metrics` proves the endpoint + scrape actually work each CI run. The alternative — pushing metrics over OTLP to a collector that re-exposes them — was already rejected in ADR-0023 (no collector hop). Not revisited here. ## Consequences **Positive** - Golden-signal metrics on `/metrics` with the standard OTel names (`http_server_request_duration_seconds`, `dotnet_*`), scraped straight by Prometheus. - No collector, no bespoke exposition code. **Negative / costs** - A `-beta` package in production services. Mitigated by the pin + the `verify-metrics` CI gate; upgrading tracks the OTel core version bumps. ## Coupling rules touched (CLAUDE.md §8) None. Metrics are passive: Prometheus pulls; no service calls into the stack.