Spec: Prometheus metrics¶
What it does¶
iiiris optionally exports operational metrics in Prometheus exposition
format at a GET /metrics endpoint. Metrics are in-process counters
scraped per replica — no shared state, no external backend, no
persistence; Prometheus aggregates across replicas via instance labels
and handles counter resets natively. The subsystem is off by default
and its absence leaves the request path entirely uninstrumented.
Surface¶
HTTP¶
GET <metrics.path>(default/metrics) — Prometheus exposition, served bypromhttpover a dedicated registry.- With
metrics.addrunset the route rides the main listener (peer to/healthand/ready). Withmetrics.addrset (e.g.":9090") the endpoint is served by a dedicated listener at that address and is not mounted on the main one. The dedicated listener shares the main server's read/write timeouts and shuts down with it. - With
metrics.usernameandmetrics.passwordboth set, the endpoint requires HTTP Basic auth (constant-time comparison,WWW-Authenticatechallenge on failure). Otherwise it is open.
Configuration¶
metrics:
enabled: false # master switch; everything below ignored when false
path: /metrics # route; empty falls back to /metrics
addr: "" # non-empty = dedicated listener instead of the main one
username: "" # both set = HTTP Basic required
password: ""
Env overrides: IIIRIS_METRICS_ENABLED, IIIRIS_METRICS_PATH,
IIIRIS_METRICS_ADDR, IIIRIS_METRICS_USER, IIIRIS_METRICS_PASS.
Go surface¶
internal/metrics:New(version) *Metrics(registry + all families),(*Metrics).Handler(user, pass) http.Handler, observer methods/factories (ObserveHTTP,CacheObserver,SourceObserver,ObservePipelineExecute,ObservePipelineQueueWait,ObserveAuthDecision), scrape-time collector registration (RegisterCacheFootprint,RegisterSourceHealth). Methods with a*Metricsreceiver are nil-safe no-ops.- Observer hooks in instrumented packages (all plain funcs or structs
of funcs; no metrics-library types):
cache.OpObserver+cache.Observed{Render,Info,Origin}wrappers,source.OpenObserver+source.Observedwrapper (Unwrap- transparent),image.Observervia(*Pipeline).SetObserver,server.Deps.ObserveRequest/.MetricsHandler/.MetricsPath, and the pre-existingauth.RuleAuthorizer.Recorder. cmd/iiirisdwires everything; no instrumentation is active whenmetrics.enabledis false.
Metric families¶
Namespace iiiris_. These names, types, and label sets are the wire
contract.
| Family | Type | Labels |
|---|---|---|
iiiris_http_requests_total |
counter | route, code (1xx…5xx) |
iiiris_http_request_duration_seconds |
histogram | route |
iiiris_http_response_bytes_total |
counter | route |
iiiris_cache_ops_total |
counter | cache (render/info/origin/manifest), backend, op (hit/miss/put/delete/error) |
iiiris_cache_entries, iiiris_cache_bytes |
gauge | cache, backend |
iiiris_source_open_total |
counter | source, backend, outcome (ok/not_found/error) |
iiiris_source_open_duration_seconds |
histogram | source, backend |
iiiris_pipeline_execute_duration_seconds |
histogram | kind (thumbnail/reduced/full) |
iiiris_pipeline_queue_wait_seconds |
histogram | — |
iiiris_overlay_applied_total |
counter | target (output/source), fit (none/shrink/scale/tile) |
iiiris_overlay_composite_seconds |
histogram | — |
iiiris_auth_decisions_total |
counter | profile, outcome (allow/deny) |
iiiris_source_healthy |
gauge (0/1) | source |
iiiris_build_info |
gauge (const 1) | version, go_version |
go_*, process_* |
— | client_golang default collectors |
Contracts¶
- Off by default.
metrics.enabled: falseinDefault(); the zero-config path registers no route and wires no observers. prometheus/client_golanghas exactly one consumer:internal/metrics. Instrumented packages depend only on their own plain-func observer types.- Bounded label cardinality.
routeis aclassifyRouteclass,cacheis one of four roles,backend/source/profilecome from configuration. Never per-identifier, per-path, or per-client labels. - Single instrumentation point for HTTP. The Prometheus HTTP
families and the admin dashboard's per-route stats are fed from the
same
countRequestsmiddleware call, so the two surfaces cannot drift. RequestsclassifyRoutedeclines to track (the admin SSE stream) appear in neither. - Dedicated registry, never the client library's global default.
metrics.addris exclusive: set, the endpoint exists only on the dedicated listener.- Basic auth requires both credentials. A half-configured pair leaves the endpoint open.
- Pipeline timing covers successful renders only, labeled by the decode path actually taken; failures surface through the HTTP error metrics. Queue wait is reported separately and excluded from the execute duration.
- Source open is measured at the upstream — inside any
source.Cachedwrapper — so it reflects true origin latency (time-to-response, not body transfer). Origin-cache effectiveness isiiiris_cache_ops_total{cache="origin"}. - Footprint gauges read
cache.Staterat scrape time. Backends withoutStater(S3) get no footprint gauge; a backend reporting bytes-1(Redis) getsiiiris_cache_entriesbut noiiiris_cache_bytes. The filesystem backend's tree walk runs once per scrape. iiiris_source_healthyruns the same bounded healthcheck as/readyonce per scrape.- Observed cache wrappers do not forward
Stater/Purger; the admin UI and the footprint collectors take the unwrapped caches. - Observer callbacks run on the request path and must stay cheap and non-blocking.
- Histogram buckets are fixed (client default ~5 ms–10 s ladders; queue wait 1 ms–5 s), not operator-configurable.
Out of scope¶
- OpenTelemetry SDK — dependency weight and abstraction unneeded
for a metrics-only goal; OTel pipelines scrape this endpoint via the
collector's Prometheus receiver. OTel tracing is a separate,
opt-in subsystem (
internal/telemetry; seetelemetry.md) and does not touch this endpoint. - Push delivery (Pushgateway / remote-write) — pull is the standard for long-lived servers.
- Per-identifier or per-path labels — unbounded cardinality; permanently out, not deferred.
- Configurable histogram buckets — add only on demonstrated need.
- Admin dashboard backed by Prometheus — the SSE dashboard keeps
its own
server.Stats(it needs on-demand quantile snapshots). - Metrics persistence across restarts — counter resets are native to the Prometheus model.