Metrics scraping
This page documents Orleans.Lattice.Api.Mcp.RepoContext, which is unreleased, in the documentation for Orleans.Lattice 9.9.0 (release line 9.9), built 2026-10-04. It is also published as markdown, with every table and list, at metrics-scraping.md, and llms.txt lists every page.Part of Container quickstart.
GET /metrics serves a Prometheus text exposition (text/plain; version=0.0.4) on the same listener as MCP and the health probes, so a scraper needs no second port and no sidecar. Like the probes it is unauthenticated and always on: the listener is expected to sit on a private network, exactly as the sample compose file wires it.
The endpoint exposes every instrument published on a meter whose name starts with one of three prefixes, matched case-insensitively: orleans.lattice, which covers the core meter and every per-package meter, Orleans.Lattice.Api.Mcp.RepoContext included; Microsoft.Orleans, the Orleans runtime's own meters; and System.Runtime, the base class library's runtime meter, which carries garbage-collection, working-set, allocation, JIT, thread-pool and exception series. Instruments are selected by meter name, never by meter instance, so an instrument is exposed regardless of which type created it, and no package reference is needed for any of the three. This host's own instruments - the lattice_repocontext_* series on this page - are published on a single meter, orleans.lattice.repocontext.host, which the orleans.lattice prefix already covers: subscribe to it with AddMeter("orleans.lattice.repocontext.host") to export them elsewhere, or filter a scrape on the lattice_repocontext_ name prefix, because series are named from the instrument rather than the meter. Unlike the library's instruments they carry no tenant tag, because each describes the host process rather than any tenant's traffic. The four lattice_metrics_* self-report series below are on no meter at all - the endpoint renders them itself - so they exist only on this scrape.
The last two were added by issue #2543, and they are why a container-level memory question is now answerable from inside. The listener previously matched orleans.lattice alone, so every measurement the .NET and Orleans runtimes published was enumerated and then discarded at the subscription predicate. The effect was not a degraded runtime view but the complete absence of one: no heap size, no working set, no per-generation collection counts, no allocation rate, and nothing whatever from Orleans. The families now on the endpoint are named dotnet_* - dotnet_gc_last_collection_heap_size, dotnet_process_memory_working_set, and dotnet_gc_collections_total tagged by generation, among others - and microsoft_orleans_*.
Which prefixes a build subscribes to is itself a series, because that question could otherwise be answered only by reading the image's source:
| Gauge | Meaning |
|---|---|
lattice_metrics_subscribed_meters |
Distinct meters that have published at least one instrument under each subscribed prefix, labelled prefix. |
Read it three ways, and the third is the one it exists for. Absent means this build does not subscribe to that prefix at all, so an older image or a revert. Zero means the build subscribes and no meter has published under it, which is the honest reading on a host with no Orleans silo in it. A positive count says how many meters matched. Every prefix is minted at zero when the collector is constructed, before the listener starts, so the zero is always present and can never be read as the absence. Without that mint the two render identically, and an empty runtime section on a dashboard would be unattributable between "nothing published" and "this deployment does not collect it".
Three properties are worth knowing when reading a scrape:
An instrument that has never recorded a measurement still announces itself with
# HELPand# TYPElines and no samples, so "the instrument is absent" and "the instrument has not fired yet" are distinguishable from the payload alone.A
Histogram<T>renders as a Prometheussummarycarrying_sumand_count, and no_bucketseries. The listener reports raw measurements and does not surface bucket boundaries - not even the explicit boundariesorleans.lattice.wal.replay.permit_queue_waithas declared asInstrumentAdvicesince issue #3044, which only an exporter that reads advice (such as the OpenTelemetry Prometheus exporter) can honour - so emitting ahistogramfamily would mean inventing buckets and reporting invented quantiles as measurements.This has a consequence worth stating plainly, because it fabricates a plausible number rather than an obvious gap. A PromQL
histogram_quantileover a_bucketseries returns nothing here, and the common dashboard idiom of appendingor vector(0)then substitutes a literal zero. The shippedOrleansLatticeCommitPathdashboard does exactly that fororleans_lattice_leaf_deactivation_checkpoint_delta, so scraped from this endpoint its p95 panel reads a flat zero - which is indistinguishable from the sustained-zero cold-arm fault shape that same dashboard tells you to look for. Read_sumand_countfrom this endpoint and treat any quantile panel as unavailable, not as measured. A pipeline that needs true quantiles needs a real histogram exporter, not this endpoint.The endpoint self-reports its own limits, so a truncated scrape says so rather than reading as a quiet zero:
Instrument Meaning lattice_metrics_seriesLive series currently held by the collector. lattice_metrics_dropped_measurements_totalMeasurements dropped since start because a ceiling was reached. lattice_metrics_dropped_measurements_by_family_totalThe same drops attributed to the metric family that caused them, labelled family, and to the ceiling that refused them, labelledceiling(familyorglobal).The counters say that a ceiling was reached, but not when. That instant is what decides which historical absences can be read: before it, an absent series is a real absence, and after it, the series may simply have been refused. So the collector also logs the transition itself (issue #2519). The first time each ceiling refuses a series, it writes exactly one
Warningwith event id1/MetricsCeilingReachedunder theOrleans.Lattice.Api.Mcp.RepoContext.Host.RepoContextMetricsCollectorcategory. The record carries these fields:Ceiling:familyorglobal.Limit: the ceiling's value.SaturatedAtUtc: the instant of the first refusal.Family: the refused family.FamilySeriesandTotalSeries: the series counts at that moment.
The per-family ceiling announces once per family, and the global backstop announces once per process. A crossing that happens during startup, before logging is wired up, is written as soon as the logger is attached, still stamped with its original instant. To find the boundary, search the log for
MetricsCeilingReached: absences in that family (or in every family, forglobal) are unambiguous only before itsSaturatedAtUtc.