From f5f21c213c7dd7298289f770452053ba97ec40e3 Mon Sep 17 00:00:00 2001 From: Mario Minardi Date: Thu, 7 Dec 2023 09:24:25 -0700 Subject: [PATCH] client/web: add additional web client metrics logging (#10462) Add additional web client metric logging. Namely, add logging events for auth / deauth, enable / disable using exit node, enable / disable SSH, enable / disable advertise routes, and click events on the device details button. Updates https://github.com/tailscale/tailscale/issues/10261 Signed-off-by: Mario Minardi --- client/web/src/api.ts | 8 ++++++ .../components/views/device-details-view.tsx | 8 +++--- client/web/src/components/views/home-view.tsx | 7 +++++- .../web/src/components/views/login-view.tsx | 7 ++++-- client/web/src/hooks/node-data.ts | 25 ++++++++++++++++--- 5 files changed, 46 insertions(+), 9 deletions(-) diff --git a/client/web/src/api.ts b/client/web/src/api.ts index 95d3afc91..410a71d5e 100644 --- a/client/web/src/api.ts +++ b/client/web/src/api.ts @@ -119,3 +119,11 @@ type MetricType = "counter" | "gauge" export type MetricName = | "web_client_advertise_exitnode_enable" | "web_client_advertise_exitnode_disable" + | "web_client_use_exitnode_enable" + | "web_client_use_exitnode_disable" + | "web_client_ssh_enable" + | "web_client_ssh_disable" + | "web_client_node_connect" + | "web_client_node_disconnect" + | "web_client_advertise_routes_change" + | "web_client_device_details_click" diff --git a/client/web/src/components/views/device-details-view.tsx b/client/web/src/components/views/device-details-view.tsx index d556b179e..83f431a4a 100644 --- a/client/web/src/components/views/device-details-view.tsx +++ b/client/web/src/components/views/device-details-view.tsx @@ -3,7 +3,7 @@ import cx from "classnames" import React from "react" -import { apiFetch } from "src/api" +import { apiFetch, incrementMetric } from "src/api" import ACLTag from "src/components/acl-tag" import * as Control from "src/components/control-components" import NiceIP from "src/components/nice-ip" @@ -40,11 +40,13 @@ export default function DeviceDetailsView({ {!readonly && ( diff --git a/client/web/src/components/views/home-view.tsx b/client/web/src/components/views/home-view.tsx index 30c431cb7..97eb9ebb2 100644 --- a/client/web/src/components/views/home-view.tsx +++ b/client/web/src/components/views/home-view.tsx @@ -3,6 +3,7 @@ import cx from "classnames" import React, { useMemo } from "react" +import { incrementMetric } from "src/api" import { ReactComponent as ArrowRight } from "src/assets/icons/arrow-right.svg" import { ReactComponent as Machine } from "src/assets/icons/machine.svg" import AddressCard from "src/components/address-copy-card" @@ -68,7 +69,11 @@ export default function HomeView({ disabled={readonly} /> )} - + incrementMetric("web_client_device_details_click")} + > View device details → diff --git a/client/web/src/components/views/login-view.tsx b/client/web/src/components/views/login-view.tsx index e18f7b03c..8537a662d 100644 --- a/client/web/src/components/views/login-view.tsx +++ b/client/web/src/components/views/login-view.tsx @@ -2,7 +2,7 @@ // SPDX-License-Identifier: BSD-3-Clause import React, { useCallback, useState } from "react" -import { apiFetch } from "src/api" +import { apiFetch, incrementMetric } from "src/api" import { ReactComponent as TailscaleIcon } from "src/assets/icons/tailscale-icon.svg" import { NodeData } from "src/hooks/node-data" import Button from "src/ui/button" @@ -25,7 +25,10 @@ export default function LoginView({ const login = useCallback( (opt: TailscaleUpOptions) => { - tailscaleUp(opt).then(refreshData) + tailscaleUp(opt).then(() => { + incrementMetric("web_client_node_connect") + refreshData() + }) }, [refreshData] ) diff --git a/client/web/src/hooks/node-data.ts b/client/web/src/hooks/node-data.ts index 3ad1646f2..f0237a6d1 100644 --- a/client/web/src/hooks/node-data.ts +++ b/client/web/src/hooks/node-data.ts @@ -148,9 +148,20 @@ export default function useNodeData() { setIsPosting(false) mutate() // refresh data after PATCH finishes } + const updateMetrics = () => { + // only update metrics if values have changed + if (data?.RunningSSHServer !== d.RunSSH) { + incrementMetric( + d.RunSSH ? "web_client_ssh_enable" : "web_client_ssh_disable" + ) + } + } return apiFetch("/local/v0/prefs", "PATCH", d) - .then(onComplete) + .then(() => { + updateMetrics() + onComplete() + }) .catch((err) => { onComplete() alert("Failed to update prefs") @@ -176,6 +187,14 @@ export default function useNodeData() { : "web_client_advertise_exitnode_disable" ) } + // useExitNode is the ID of the exit node to use + if (data?.UsingExitNode?.ID !== d.UseExitNode) { + incrementMetric( + d.UseExitNode + ? "web_client_use_exitnode_enable" + : "web_client_use_exitnode_disable" + ) + } } return apiFetch("/routes", "POST", d) @@ -189,7 +208,7 @@ export default function useNodeData() { throw err }) }, - [mutate, data?.AdvertisingExitNode] + [mutate, data?.AdvertisingExitNode, data?.UsingExitNode?.ID] ) const nodeUpdaters: NodeUpdaters = useMemo( @@ -209,7 +228,7 @@ export default function useNodeData() { AdvertiseRoutes: routes, AdvertiseExitNode: data?.AdvertisingExitNode, // unchanged UseExitNode: data?.UsingExitNode?.ID, // unchanged - }), + }).then(() => incrementMetric("web_client_advertise_routes_change")), }), [ data?.AdvertisingExitNode,