diff options
| -rw-r--r-- | tsweb/promvarz/promvarz.go | 10 | ||||
| -rw-r--r-- | tsweb/promvarz/promvarz_test.go | 66 |
2 files changed, 76 insertions, 0 deletions
diff --git a/tsweb/promvarz/promvarz.go b/tsweb/promvarz/promvarz.go index 9740aeeca..3166d6baa 100644 --- a/tsweb/promvarz/promvarz.go +++ b/tsweb/promvarz/promvarz.go @@ -14,6 +14,13 @@ import ( "tailscale.com/tsweb/varz" ) +// OmitPromethusMetrics, if set to true, makes Handler not include native +// Prometheus metrics. +// +// This is useful in some specific cases where the built-in Prometheus +// collectors have poor performance characteristics. +var OmitPromethusMetrics bool + // Handler returns Prometheus metrics exported by our expvar converter // and the official Prometheus client. func Handler(w http.ResponseWriter, r *http.Request) { @@ -28,6 +35,9 @@ func Handler(w http.ResponseWriter, r *http.Request) { // gatherNativePrometheusMetrics writes metrics from the default // metric registry in text format. func gatherNativePrometheusMetrics(w http.ResponseWriter) error { + if OmitPromethusMetrics { + return nil + } enc := expfmt.NewEncoder(w, expfmt.FmtText) mfs, err := prometheus.DefaultGatherer.Gather() if err != nil { diff --git a/tsweb/promvarz/promvarz_test.go b/tsweb/promvarz/promvarz_test.go index a3f4e66f1..f21420ee5 100644 --- a/tsweb/promvarz/promvarz_test.go +++ b/tsweb/promvarz/promvarz_test.go @@ -6,12 +6,15 @@ import ( "expvar" "net/http" "net/http/httptest" + "runtime" "strings" "testing" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" "github.com/prometheus/client_golang/prometheus/testutil" + "github.com/prometheus/common/expfmt" + "tailscale.com/tstest" ) var ( @@ -35,4 +38,67 @@ func TestHandler(t *testing.T) { if err := testutil.ScrapeAndCompare(svr.URL, strings.NewReader(want), "promvarz_test_expvar", "promvarz_test_native"); err != nil { t.Error(err) } + + // By default, we include Prometheus's process metrics; these are only + // published on Linux, so check that they're present. + // + // If we ever change this behaviour, feel free to change or remove this + // test; it's only here so that the TestOmitPromethusMetrics test can + // check that it's working. + if runtime.GOOS == "linux" && !hasProcessMetrics(t, svr.URL) { + t.Error("process metrics not found") + } +} + +// TestOmitPromethusMetrics verifies that OmitPromethusMetrics works correctly. +func TestOmitPromethusMetrics(t *testing.T) { + if runtime.GOOS != "linux" { + t.Skip("process metrics are only published on Linux") + } + + tstest.Replace(t, &OmitPromethusMetrics, true) + testVar1.Set(42) + + svr := httptest.NewServer(http.HandlerFunc(Handler)) + defer svr.Close() + + want := ` + # TYPE promvarz_test_expvar gauge + promvarz_test_expvar 42 + ` + if err := testutil.ScrapeAndCompare(svr.URL, strings.NewReader(want), "promvarz_test_expvar"); err != nil { + t.Error(err) + } + + if hasProcessMetrics(t, svr.URL) { + t.Error("process metrics unexpectedly found") + } +} + +// hasProcessMetrics checks if metrics from the Prometheus process collector +// are present at the given metrics URL. +func hasProcessMetrics(tb testing.TB, url string) bool { + resp, err := http.Get(url) + if err != nil { + tb.Errorf("scraping metrics failed: %v", err) + return false + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + tb.Errorf("the scraping target returned a status code other than 200: %d", + resp.StatusCode) + return false + } + + var tp expfmt.TextParser + metrics, err := tp.TextToMetricFamilies(resp.Body) + if err != nil { + tb.Errorf("converting body to metric families failed: %v", err) + return false + } + if _, found := metrics["process_open_fds"]; found { + return true + } + return false } |
