tsweb: add PrometheusVar, for vars that want to output varz themselves.

This enables the infrequent use of more complex Prometheus types, such as
timeseries with high/irregular label cardinality, without needing to
discover and implement generic abstracted type like LabelMap for each one.

Signed-off-by: David Anderson <danderson@tailscale.com>
pull/4235/head
David Anderson 2 years ago committed by Dave Anderson
parent f6642e0ece
commit 7b4960316b

@ -349,6 +349,15 @@ func Error(code int, msg string, err error) HTTPError {
return HTTPError{Code: code, Msg: msg, Err: err} return HTTPError{Code: code, Msg: msg, Err: err}
} }
// PrometheusVar is a value that knows how to format itself into
// Prometheus metric syntax.
type PrometheusVar interface {
// WritePrometheus writes the value of the var to w, in Prometheus
// metric syntax. All variables names written out must start with
// prefix (or write out a single variable named exactly prefix)
WritePrometheus(w io.Writer, prefix string)
}
// WritePrometheusExpvar writes kv to w in Prometheus metrics format. // WritePrometheusExpvar writes kv to w in Prometheus metrics format.
// //
// See VarzHandler for conventions. This is exported primarily for // See VarzHandler for conventions. This is exported primarily for
@ -379,6 +388,9 @@ func writePromExpVar(w io.Writer, prefix string, kv expvar.KeyValue) {
name := prefix + key name := prefix + key
switch v := kv.Value.(type) { switch v := kv.Value.(type) {
case PrometheusVar:
v.WritePrometheus(w, name)
return
case *expvar.Int: case *expvar.Int:
if typ == "" { if typ == "" {
typ = "counter" typ = "counter"

@ -9,6 +9,8 @@ import (
"context" "context"
"errors" "errors"
"expvar" "expvar"
"fmt"
"io"
"net" "net"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
@ -469,6 +471,12 @@ foo_AUint16 65535
expvar.Func(func() any { return 123 }), expvar.Func(func() any { return 123 }),
"num_goroutines 123\n", "num_goroutines 123\n",
}, },
{
"var_that_exports_itself",
"custom_var",
promWriter{},
"custom_var_value 42\n",
},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
@ -534,3 +542,13 @@ func (expvarAdapter) String() string { return "{}" } // expvar JSON; unused in t
func (a expvarAdapter) PrometheusMetricsReflectRoot() any { func (a expvarAdapter) PrometheusMetricsReflectRoot() any {
return a.st return a.st
} }
type promWriter struct{}
func (promWriter) WritePrometheus(w io.Writer, prefix string) {
fmt.Fprintf(w, "%s_value 42\n", prefix)
}
func (promWriter) String() string {
return ""
}

Loading…
Cancel
Save