diff --git a/client/tailscale/localclient.go b/client/tailscale/localclient.go index fba916804..150eed0ed 100644 --- a/client/tailscale/localclient.go +++ b/client/tailscale/localclient.go @@ -259,6 +259,29 @@ func (lc *LocalClient) DaemonMetrics(ctx context.Context) ([]byte, error) { return lc.get200(ctx, "/localapi/v0/metrics") } +// IncrementMetric increments the value of a Tailscale daemon's metric by +// the given delta. If the metric has yet to exist, a new counter metric is +// created and initialized to delta. +// +// IncrementMetric only supports counter metrics and non-negative delta values. +// Gauge metrics are unsupported. +func (lc *LocalClient) IncrementMetric(ctx context.Context, name string, delta int) error { + type metricUpdate struct { + Name string `json:"name"` + Type string `json:"type"` + Value int `json:"value"` // amount to increment by + } + if delta < 0 { + return errors.New("negative delta not allowed") + } + _, err := lc.send(ctx, "POST", "/localapi/v0/upload-client-metrics", 200, jsonBody([]metricUpdate{{ + Name: name, + Type: "counter", + Value: delta, + }})) + return err +} + // TailDaemonLogs returns a stream the Tailscale daemon's logs as they arrive. // Close the context to stop the stream. func (lc *LocalClient) TailDaemonLogs(ctx context.Context) (io.Reader, error) { diff --git a/ipn/localapi/localapi.go b/ipn/localapi/localapi.go index a3bda73f7..7ad1b5522 100644 --- a/ipn/localapi/localapi.go +++ b/ipn/localapi/localapi.go @@ -1458,10 +1458,9 @@ func (h *Handler) serveUploadClientMetrics(w http.ResponseWriter, r *http.Reques return } type clientMetricJSON struct { - Name string `json:"name"` - // One of "counter" or "gauge" - Type string `json:"type"` - Value int `json:"value"` + 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