Files
tailscale/tsweb
Brad Fitzpatrick f92ca3f545 tsweb: add /debug/runtime-metrics page exposing Go runtime/metrics
The tsweb DebugHandler already links to expvar, Prometheus varz, and
pprof, but the only runtime visibility beyond pprof was the handful of
runtime.MemStats fields that varz special-cases. The runtime/metrics
package has far more (GC CPU classes, scheduler latencies, stop-the-world
pause histograms, heap breakdowns, and so on) and is the runtime's
preferred, cheaper interface.

Add a /debug/runtime-metrics handler. By default it serves only an index
of metric names, kinds, and descriptions, which are static, so viewing
the page does not read any values. The index is an HTML table for
browsers (Accept: text/html) and plain text otherwise; format=html or
format=text overrides the sniffing.

Values are read only on request, always as JSON:

  - /debug/runtime-metrics/gc/heap/allocs:bytes returns that metric's
    bare value, handy for scripts and curl.
  - /debug/runtime-metrics?name=NAME (repeatable) returns a JSON object
    keyed by metric name. A trailing * matches by prefix, so name=/gc/*
    returns the GC metrics and name=* returns everything.

Histograms are objects with counts and buckets arrays; infinite bucket
boundaries, which JSON cannot represent, are the strings "-Inf" and
"+Inf".

In the HTML index, exact metric names link to the bare value form, and
each run of metrics sharing a directory is headed by a row linking every
ancestor directory to its wildcard query (/gc/*, /gc/heap/*, ...).

Responses inherit the debug handler's nosniff, framing, and CSP headers,
and application/json is not a script MIME type, so the values cannot be
pulled in cross-origin as a script by a malicious page.

Like the pprof handlers, this is excluded from js/wasm builds to keep
them small.

Updates #21300

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Change-Id: I7c2e9f4a1b8d3e6f0a5c9b2d4e7f1a3c6b8d0e2f
2026-09-15 15:26:06 -07:00
..
2026-07-10 17:39:16 -07:00