tsweb/varz: add node_boot_time_seconds expvar

Export the machine's boot time (the btime line from Linux's
/proc/stat) as node_boot_time_seconds, named to match what
Prometheus's node exporter uses for the same value. Combined with
process_start_unix_time, this can be used to distinguish process
restarts from whole node restarts.

The value is parsed once per process lifetime, not per scrape, and
the metric is only published when a value is available, so non-Linux
systems don't export a bogus zero.

Updates tailscale/corp#44743

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Change-Id: I5f53186b97bb1482bd1a5387c0910b0ae26544ff
This commit is contained in:
Brad Fitzpatrick
2026-07-10 15:18:55 +00:00
committed by Brad Fitzpatrick
parent 16f600df8c
commit 0e79b322a9

View File

@@ -42,8 +42,34 @@ func init() {
expvar.Publish("go_version", StaticStringVar(runtime.Version()))
expvar.Publish("counter_uptime_sec", expvar.Func(func() any { return int64(Uptime().Seconds()) }))
expvar.Publish("gauge_goroutines", expvar.Func(func() any { return runtime.NumGoroutine() }))
if v := nodeBootTime(); v != 0 {
var vi any = v // box once
// The name matches what Prometheus's node exporter uses
// for the same value.
expvar.Publish("node_boot_time_seconds", expvar.Func(func() any { return vi }))
}
}
// nodeBootTime returns the machine's boot time in Unix seconds,
// as reported by the "btime" line of Linux's /proc/stat.
// It returns 0 if unavailable, such as on non-Linux systems.
var nodeBootTime = sync.OnceValue(func() int64 {
stat, err := os.ReadFile("/proc/stat")
if err != nil {
return 0
}
for line := range strings.Lines(string(stat)) {
if rest, ok := strings.CutPrefix(line, "btime "); ok {
sec, err := strconv.ParseInt(strings.TrimSpace(rest), 10, 64)
if err != nil {
return 0
}
return sec
}
}
return 0
})
const (
gaugePrefix = "gauge_"
counterPrefix = "counter_"