From 6b083a8ddfcfac43f93bd180356b262fcbcdc280 Mon Sep 17 00:00:00 2001 From: Mario Minardi Date: Tue, 5 Dec 2023 08:28:19 -0700 Subject: [PATCH] client/web: add metric logging logic to the web client (#10434) Add metric logging logic for the web client frontend. This is an initial pass of adding the base logic, plus a single point where it is used for validation that the logging is working correctly. More metric logging calls will follow in subsquent PRs. Updates https://github.com/tailscale/tailscale/issues/10261 Signed-off-by: Mario Minardi --- client/web/src/api.ts | 27 +++++++++++++++++++++++++++ client/web/src/hooks/node-data.ts | 14 ++++++++++++-- client/web/web.go | 16 +--------------- 3 files changed, 40 insertions(+), 17 deletions(-) diff --git a/client/web/src/api.ts b/client/web/src/api.ts index b6c0c4415..f772f2cee 100644 --- a/client/web/src/api.ts +++ b/client/web/src/api.ts @@ -77,3 +77,30 @@ export function setSynoToken(token?: string) { export function setUnraidCsrfToken(token?: string) { unraidCsrfToken = token } + +// incrementMetric hits the client metrics local API endpoint to +// increment the given counter metric by one. +export function incrementMetric(metricName: MetricName) { + const postData : MetricsPOSTData[] = [{ + Name: metricName, + Type: "counter", + Value: 1 + }] + + apiFetch("/local/v0/upload-client-metrics", "POST", postData) + .catch((error) => { + console.error(error) + }) +} + +type MetricsPOSTData = { + Name: MetricName + Type: MetricType + Value: number +} + +type MetricType = "counter" | "gauge" + +export type MetricName = + | "web_client_advertise_exitnode_enable" + | "web_client_advertise_exitnode_disable" diff --git a/client/web/src/hooks/node-data.ts b/client/web/src/hooks/node-data.ts index 50cf82c5d..a5825cac2 100644 --- a/client/web/src/hooks/node-data.ts +++ b/client/web/src/hooks/node-data.ts @@ -6,6 +6,7 @@ import { apiFetch, setUnraidCsrfToken } from "src/api" import { ExitNode, noExitNode, runAsExitNode } from "src/hooks/exit-nodes" import { VersionInfo } from "src/hooks/self-update" import { assertNever } from "src/util" +import { incrementMetric, MetricName } from "src/api" export type NodeData = { Profile: UserProfile @@ -173,16 +174,25 @@ export default function useNodeData() { setIsPosting(false) refreshData() // refresh data after POST finishes } + const updateMetrics = () => { + // only update metrics if values have changed + if (data?.AdvertisingExitNode !== d.AdvertiseExitNode) { + incrementMetric(d.AdvertiseExitNode ? "web_client_advertise_exitnode_enable" : "web_client_advertise_exitnode_disable") + } + } return apiFetch("/routes", "POST", d) - .then(onComplete) + .then(() => { + updateMetrics() + onComplete() + }) .catch((err) => { onComplete() alert("Failed to update routes") throw err }) }, - [setIsPosting, refreshData] + [setIsPosting, refreshData, data?.AdvertisingExitNode] ) useEffect( diff --git a/client/web/web.go b/client/web/web.go index 767d08794..e17ab7fc2 100644 --- a/client/web/web.go +++ b/client/web/web.go @@ -759,12 +759,6 @@ func (s *Server) servePostRoutes(w http.ResponseWriter, r *http.Request) { return } - oldPrefs, err := s.lc.GetPrefs(r.Context()) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - // Calculate routes. routesStr := strings.Join(data.AdvertiseRoutes, ",") routes, err := netutil.CalcAdvertiseRoutes(routesStr, data.AdvertiseExitNode) @@ -797,15 +791,6 @@ func (s *Server) servePostRoutes(w http.ResponseWriter, r *http.Request) { return } - // Report metrics. - if data.AdvertiseExitNode != hasExitNodeRoute(oldPrefs.AdvertiseRoutes) { - if data.AdvertiseExitNode { - s.lc.IncrementCounter(r.Context(), "web_client_advertise_exitnode_enable", 1) - } else { - s.lc.IncrementCounter(r.Context(), "web_client_advertise_exitnode_disable", 1) - } - } - w.WriteHeader(http.StatusOK) } @@ -979,6 +964,7 @@ var localapiAllowlist = []string{ "/v0/update/check", "/v0/update/install", "/v0/update/progress", + "/v0/upload-client-metrics", } // csrfKey returns a key that can be used for CSRF protection.