tsweb: expose native Prometheus metrics in /debug/varz

The handler will expose built-in process and Go metrics by default,
which currently duplicate some of the expvar-proxied metrics
(`goroutines` vs `go_goroutines`, `memstats` vs `go_memstats`), but as
long as their names are different, Prometheus server will just scrape
both.

This will change /debug/varz behaviour for most tsweb binaries, but
notably not for control, which configures a `tsweb.VarzHandler`
[explicitly](a5b5d5167f/cmd/tailcontrol/tailcontrol.go (L779))

Updates https://github.com/tailscale/corp/issues/10205

Signed-off-by: Anton Tolchanov <anton@tailscale.com>
This commit is contained in:
Anton Tolchanov
2023-04-03 11:23:37 +01:00
committed by Anton Tolchanov
parent 690446c784
commit 11e6247d2a
6 changed files with 155 additions and 39 deletions

View File

@@ -26,6 +26,8 @@ import (
"sync"
"time"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/expfmt"
"go4.org/mem"
"tailscale.com/envknob"
"tailscale.com/metrics"
@@ -672,6 +674,39 @@ func VarzHandler(w http.ResponseWriter, r *http.Request) {
}
}
// CombinedVarzHandler is an HTTP handler for Prometheus metrics that
// combines native metrics with the ones converted from expvar.
func CombinedVarzHandler(w http.ResponseWriter, r *http.Request) {
if err := gatherNativePrometheusMetrics(w); err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
VarzHandler(w, r)
}
// gatherNativePrometheusMetrics writes metrics from the default
// metric registry in text format.
func gatherNativePrometheusMetrics(w http.ResponseWriter) error {
enc := expfmt.NewEncoder(w, expfmt.FmtText)
mfs, err := prometheus.DefaultGatherer.Gather()
if err != nil {
return fmt.Errorf("could not gather metrics from DefaultGatherer: %w", err)
}
for _, mf := range mfs {
if err := enc.Encode(mf); err != nil {
return fmt.Errorf("could not encode metric %v: %w", mf, err)
}
}
if closer, ok := enc.(expfmt.Closer); ok {
if err := closer.Close(); err != nil {
return err
}
}
return nil
}
// PrometheusMetricsReflectRooter is an optional interface that expvar.Var implementations
// can implement to indicate that they should be walked recursively with reflect to find
// sets of fields to export.