From 0e79b322a9c9782fa122b37fd2b12bcb2dd6df28 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Fri, 10 Jul 2026 15:18:55 +0000 Subject: [PATCH] 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 Change-Id: I5f53186b97bb1482bd1a5387c0910b0ae26544ff --- tsweb/varz/varz.go | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tsweb/varz/varz.go b/tsweb/varz/varz.go index 0df6e5775..0e2721ebb 100644 --- a/tsweb/varz/varz.go +++ b/tsweb/varz/varz.go @@ -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_"