tsweb: move varz handler(s) into separate modules

This splits Prometheus metric handlers exposed by tsweb into two
modules:
- `varz.Handler` exposes Prometheus metrics generated by our expvar
  converter;
- `promvarz.Handler` combines our expvar-converted metrics and native
  Prometheus metrics.

By default, tsweb will use the promvarz handler, however users can keep
using only the expvar converter. Specifically, `tailscaled` now uses
`varz.Handler` explicitly, which avoids a dependency on the
(heavyweight) Prometheus client.

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-10 11:28:16 +01:00
committed by Anton Tolchanov
parent c153e6ae2f
commit 8546ff98fb
15 changed files with 850 additions and 808 deletions

View File

@@ -0,0 +1,48 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
// Package promvarz combines Prometheus metrics exported by our expvar converter
// (tsweb/varz) with metrics exported by the official Prometheus client.
package promvarz
import (
"fmt"
"net/http"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/expfmt"
"tailscale.com/tsweb/varz"
)
// Handler returns Prometheus metrics exported by our expvar converter
// and the official Prometheus client.
func Handler(w http.ResponseWriter, r *http.Request) {
if err := gatherNativePrometheusMetrics(w); err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
varz.Handler(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
}

View File

@@ -0,0 +1,35 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
package promvarz
import (
"expvar"
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/prometheus/client_golang/prometheus/testutil"
)
func TestHandler(t *testing.T) {
test1 := expvar.NewInt("gauge_promvarz_test_expvar")
test1.Set(42)
test2 := promauto.NewGauge(prometheus.GaugeOpts{Name: "promvarz_test_native"})
test2.Set(4242)
svr := httptest.NewServer(http.HandlerFunc(Handler))
defer svr.Close()
want := `
# TYPE promvarz_test_expvar gauge
promvarz_test_expvar 42
# TYPE promvarz_test_native gauge
promvarz_test_native 4242
`
if err := testutil.ScrapeAndCompare(svr.URL, strings.NewReader(want), "promvarz_test_expvar", "promvarz_test_native"); err != nil {
t.Error(err)
}
}