client/local: add method to set gauge metric to a value
The existing client metric methods only support incrementing (or decrementing) a delta value. This new method allows setting the metric to a specific value. Updates tailscale/corp#35327 Change-Id: Ia101a4a3005adb9118051b3416f5a64a4a45987d Signed-off-by: Will Norris <will@tailscale.com>
This commit is contained in:
+17
-12
@@ -43,6 +43,7 @@ import (
|
|||||||
"tailscale.com/types/appctype"
|
"tailscale.com/types/appctype"
|
||||||
"tailscale.com/types/dnstype"
|
"tailscale.com/types/dnstype"
|
||||||
"tailscale.com/types/key"
|
"tailscale.com/types/key"
|
||||||
|
"tailscale.com/util/clientmetric"
|
||||||
"tailscale.com/util/eventbus"
|
"tailscale.com/util/eventbus"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -385,18 +386,14 @@ func (lc *Client) IncrementCounter(ctx context.Context, name string, delta int)
|
|||||||
if !buildfeatures.HasClientMetrics {
|
if !buildfeatures.HasClientMetrics {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
type metricUpdate struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
Type string `json:"type"`
|
|
||||||
Value int `json:"value"` // amount to increment by
|
|
||||||
}
|
|
||||||
if delta < 0 {
|
if delta < 0 {
|
||||||
return errors.New("negative delta not allowed")
|
return errors.New("negative delta not allowed")
|
||||||
}
|
}
|
||||||
_, err := lc.send(ctx, "POST", "/localapi/v0/upload-client-metrics", 200, jsonBody([]metricUpdate{{
|
_, err := lc.send(ctx, "POST", "/localapi/v0/upload-client-metrics", 200, jsonBody([]clientmetric.MetricUpdate{{
|
||||||
Name: name,
|
Name: name,
|
||||||
Type: "counter",
|
Type: "counter",
|
||||||
Value: delta,
|
Value: delta,
|
||||||
|
Op: "add",
|
||||||
}}))
|
}}))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -405,15 +402,23 @@ func (lc *Client) IncrementCounter(ctx context.Context, name string, delta int)
|
|||||||
// metric by the given delta. If the metric has yet to exist, a new gauge
|
// metric by the given delta. If the metric has yet to exist, a new gauge
|
||||||
// metric is created and initialized to delta. The delta value can be negative.
|
// metric is created and initialized to delta. The delta value can be negative.
|
||||||
func (lc *Client) IncrementGauge(ctx context.Context, name string, delta int) error {
|
func (lc *Client) IncrementGauge(ctx context.Context, name string, delta int) error {
|
||||||
type metricUpdate struct {
|
_, err := lc.send(ctx, "POST", "/localapi/v0/upload-client-metrics", 200, jsonBody([]clientmetric.MetricUpdate{{
|
||||||
Name string `json:"name"`
|
|
||||||
Type string `json:"type"`
|
|
||||||
Value int `json:"value"` // amount to increment by
|
|
||||||
}
|
|
||||||
_, err := lc.send(ctx, "POST", "/localapi/v0/upload-client-metrics", 200, jsonBody([]metricUpdate{{
|
|
||||||
Name: name,
|
Name: name,
|
||||||
Type: "gauge",
|
Type: "gauge",
|
||||||
Value: delta,
|
Value: delta,
|
||||||
|
Op: "add",
|
||||||
|
}}))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetGauge sets the value of a Tailscale daemon's gauge metric to the given value.
|
||||||
|
// If the metric has yet to exist, a new gauge metric is created and initialized to value.
|
||||||
|
func (lc *Client) SetGauge(ctx context.Context, name string, value int) error {
|
||||||
|
_, err := lc.send(ctx, "POST", "/localapi/v0/upload-client-metrics", 200, jsonBody([]clientmetric.MetricUpdate{{
|
||||||
|
Name: name,
|
||||||
|
Type: "gauge",
|
||||||
|
Value: value,
|
||||||
|
Op: "set",
|
||||||
}}))
|
}}))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,8 +66,8 @@ func (menu *Menu) Run(client *local.Client) {
|
|||||||
case <-menu.bgCtx.Done():
|
case <-menu.bgCtx.Done():
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
go menu.lc.IncrementGauge(menu.bgCtx, "systray_running", 1)
|
go menu.lc.SetGauge(menu.bgCtx, "systray_running", 1)
|
||||||
defer menu.lc.IncrementGauge(menu.bgCtx, "systray_running", -1)
|
defer menu.lc.SetGauge(menu.bgCtx, "systray_running", 0)
|
||||||
|
|
||||||
systray.Run(menu.onReady, menu.onExit)
|
systray.Run(menu.onReady, menu.onExit)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ tailscale.com/cmd/derper dependencies: (generated by github.com/tailscale/depawa
|
|||||||
tailscale.com/types/tkatype from tailscale.com/client/local+
|
tailscale.com/types/tkatype from tailscale.com/client/local+
|
||||||
tailscale.com/types/views from tailscale.com/ipn+
|
tailscale.com/types/views from tailscale.com/ipn+
|
||||||
tailscale.com/util/cibuild from tailscale.com/health+
|
tailscale.com/util/cibuild from tailscale.com/health+
|
||||||
tailscale.com/util/clientmetric from tailscale.com/net/netmon
|
tailscale.com/util/clientmetric from tailscale.com/net/netmon+
|
||||||
tailscale.com/util/cloudenv from tailscale.com/hostinfo+
|
tailscale.com/util/cloudenv from tailscale.com/hostinfo+
|
||||||
tailscale.com/util/ctxkey from tailscale.com/tsweb+
|
tailscale.com/util/ctxkey from tailscale.com/tsweb+
|
||||||
💣 tailscale.com/util/deephash from tailscale.com/util/syspolicy/setting
|
💣 tailscale.com/util/deephash from tailscale.com/util/syspolicy/setting
|
||||||
|
|||||||
+11
-10
@@ -1283,13 +1283,8 @@ func (h *Handler) serveUploadClientMetrics(w http.ResponseWriter, r *http.Reques
|
|||||||
http.Error(w, "unsupported method", http.StatusMethodNotAllowed)
|
http.Error(w, "unsupported method", http.StatusMethodNotAllowed)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
type clientMetricJSON struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
Type string `json:"type"` // one of "counter" or "gauge"
|
|
||||||
Value int `json:"value"` // amount to increment metric by
|
|
||||||
}
|
|
||||||
|
|
||||||
var clientMetrics []clientMetricJSON
|
var clientMetrics []clientmetric.MetricUpdate
|
||||||
if err := json.NewDecoder(r.Body).Decode(&clientMetrics); err != nil {
|
if err := json.NewDecoder(r.Body).Decode(&clientMetrics); err != nil {
|
||||||
http.Error(w, "invalid JSON body", http.StatusBadRequest)
|
http.Error(w, "invalid JSON body", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
@@ -1299,14 +1294,12 @@ func (h *Handler) serveUploadClientMetrics(w http.ResponseWriter, r *http.Reques
|
|||||||
defer metricsMu.Unlock()
|
defer metricsMu.Unlock()
|
||||||
|
|
||||||
for _, m := range clientMetrics {
|
for _, m := range clientMetrics {
|
||||||
if metric, ok := metrics[m.Name]; ok {
|
metric, ok := metrics[m.Name]
|
||||||
metric.Add(int64(m.Value))
|
if !ok {
|
||||||
} else {
|
|
||||||
if clientmetric.HasPublished(m.Name) {
|
if clientmetric.HasPublished(m.Name) {
|
||||||
http.Error(w, "Already have a metric named "+m.Name, http.StatusBadRequest)
|
http.Error(w, "Already have a metric named "+m.Name, http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var metric *clientmetric.Metric
|
|
||||||
switch m.Type {
|
switch m.Type {
|
||||||
case "counter":
|
case "counter":
|
||||||
metric = clientmetric.NewCounter(m.Name)
|
metric = clientmetric.NewCounter(m.Name)
|
||||||
@@ -1317,7 +1310,15 @@ func (h *Handler) serveUploadClientMetrics(w http.ResponseWriter, r *http.Reques
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
metrics[m.Name] = metric
|
metrics[m.Name] = metric
|
||||||
|
}
|
||||||
|
switch m.Op {
|
||||||
|
case "add", "":
|
||||||
metric.Add(int64(m.Value))
|
metric.Add(int64(m.Value))
|
||||||
|
case "set":
|
||||||
|
metric.Set(int64(m.Value))
|
||||||
|
default:
|
||||||
|
http.Error(w, "Unknown metric op "+m.Op, http.StatusBadRequest)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -58,6 +58,20 @@ const (
|
|||||||
TypeCounter
|
TypeCounter
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// MetricUpdate requests that a client metric value be updated.
|
||||||
|
//
|
||||||
|
// This is the request body sent to /localapi/v0/upload-client-metrics.
|
||||||
|
type MetricUpdate struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Type string `json:"type"` // one of "counter" or "gauge"
|
||||||
|
Value int `json:"value"` // amount to increment by or set
|
||||||
|
|
||||||
|
// Op indicates if Value is added to the existing metric value,
|
||||||
|
// or if the metric is set to Value.
|
||||||
|
// One of "add" or "set". If empty, defaults to "add".
|
||||||
|
Op string `json:"op"`
|
||||||
|
}
|
||||||
|
|
||||||
// Metric is an integer metric value that's tracked over time.
|
// Metric is an integer metric value that's tracked over time.
|
||||||
//
|
//
|
||||||
// It's safe for concurrent use.
|
// It's safe for concurrent use.
|
||||||
|
|||||||
@@ -13,6 +13,13 @@ func (*Metric) Value() int64 { return 0 }
|
|||||||
func (*Metric) Register(expvarInt any) {}
|
func (*Metric) Register(expvarInt any) {}
|
||||||
func (*Metric) UnregisterAll() {}
|
func (*Metric) UnregisterAll() {}
|
||||||
|
|
||||||
|
type MetricUpdate struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Value int `json:"value"`
|
||||||
|
Op string `json:"op"`
|
||||||
|
}
|
||||||
|
|
||||||
func HasPublished(string) bool { panic("unreachable") }
|
func HasPublished(string) bool { panic("unreachable") }
|
||||||
func EncodeLogTailMetricsDelta() string { return "" }
|
func EncodeLogTailMetricsDelta() string { return "" }
|
||||||
func WritePrometheusExpositionFormat(any) {}
|
func WritePrometheusExpositionFormat(any) {}
|
||||||
|
|||||||
Reference in New Issue
Block a user