Metrics
One store of metrics for the whole Zones process: what Zones measures of itself, and what any zone's code measures, read as Prometheus text. Off unless the shell turns it on.
// shell/next.config.mjs
export default zoneConfig({ mount: "/", metrics: true });
createZones({ metrics }) overrides the shell's declaration.
Reading it#
GET <base>/metrics (/_next-zones/metrics unless the shell's endpoints declare another base). metrics: true alone
opens it: endpoints need not be declared, and endpoints: { admin: true } is not needed. It answers an admin only: a request
with Authorization: Bearer <adminToken>, or from the same machine when no token is configured. The body is
Prometheus' text format (text/plain; version=0.0.4): point a Prometheus scraper, an OpenTelemetry collector or
any tool that reads that format at it. Nothing is pushed anywhere.
What Zones measures#
| Metric | Type | Labels | |
|---|---|---|---|
nextzones_requests_total |
counter | zone, version, code (2xx…5xx) |
Every request; zone is shell for the shell's pages and static for /_next/… files |
nextzones_request_duration_seconds |
histogram | zone |
Time to the end of each response |
nextzones_install_seconds |
histogram | zone, outcome |
Installs, from staging to serving |
nextzones_install_blocked_seconds |
histogram | zone |
The longest the event loop was held during an install |
nextzones_pull_seconds |
histogram | zone, via, outcome |
Pulls of a zone image from a source |
nextzones_pruned_images_total, nextzones_pruned_bytes_total |
counter | What pruning removed | |
nextzones_zone_active |
gauge | zone, version |
1 for the version that serves; 0 once a swap replaced it |
nextzones_registry_modules |
gauge | Server modules shared across builds | |
nextzones_registry_shared_total |
counter | Times a build used a module another build had loaded | |
nextzones_reclaims_total |
counter | Last-resort collections after a version was collected | |
process_resident_memory_bytes, nodejs_heap_used_bytes, nodejs_heap_total_bytes, nodejs_external_memory_bytes |
gauge | The process | |
nodejs_eventloop_delay_seconds |
gauge | quantile (0.5, 0.99, 1) |
Event loop delay since the last read |
process_uptime_seconds |
gauge |
Measuring in a zone#
import { counter, gauge, histogram, time } from "@runsnip/next-zones/metrics";
const exportsStarted = counter("blog_exports_total", { help: "Exports started" });
const queue = gauge("blog_queue_length", { help: "Jobs waiting" });
const size = histogram("blog_export_bytes", { help: "Export sizes", buckets: [1e4, 1e5, 1e6, 1e7] });
exportsStarted.inc({ format: "pdf" });
queue.set(12);
size.observe(file.byteLength);
const html = await time("blog_render_seconds", () => render(post), { kind: "post" }); // labelled outcome "ok" or "error"
- One store for every zone. The shell and each zone are separate builds, and each bundles this module; they all write to the one store Zones opened. Two zones may write one metric (with labels of their own).
- Off, they do nothing. With metrics off, or in the browser, or when the zone runs alone with
next start, every function returns without a trace, so a zone's code can measure unconditionally. - Prometheus' rules: a name is letters, digits,
_and:; a counter only goes up; values in base units (seconds, bytes). A name keeps the type it was first used with: using it as another type throws. - The functions:
helpis optional everywhere (Prometheus'# HELPline);counter(name, { help })→.inc(labels?),.inc(n, labels?);gauge(name, { help })→.set(value, labels?),.inc(…),.dec(…)(as a counter's);histogram(name, { help, buckets? })→.observe(value, labels?). The default buckets are seconds: a histogram of anything else (bytes, items) passes its own;time(name, fn, labels?, { help?, buckets? }?)runsfn(sync or async), returns what it returns, and observes its seconds into the histogramname, labelledoutcome"ok"or"error"(an error is thrown on);enabled()says whether the store is open;render()is the Prometheus text that/metricsserves;collect(fn)runsfnbefore each read, to set gauges from the current state, and returns a function that removes it.
- Buckets. A histogram, and
time(), use buckets in seconds unless given others (time(name, fn, labels, { buckets })): 0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, 30, 60.