mirror of
https://github.com/tailscale/tailscale.git
synced 2025-04-21 06:01:42 +00:00
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 <mario@tailscale.com>
This commit is contained in:
parent
97f84200ac
commit
f5f21c213c
@ -119,3 +119,11 @@ type MetricType = "counter" | "gauge"
|
|||||||
export type MetricName =
|
export type MetricName =
|
||||||
| "web_client_advertise_exitnode_enable"
|
| "web_client_advertise_exitnode_enable"
|
||||||
| "web_client_advertise_exitnode_disable"
|
| "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"
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
import cx from "classnames"
|
import cx from "classnames"
|
||||||
import React from "react"
|
import React from "react"
|
||||||
import { apiFetch } from "src/api"
|
import { apiFetch, incrementMetric } from "src/api"
|
||||||
import ACLTag from "src/components/acl-tag"
|
import ACLTag from "src/components/acl-tag"
|
||||||
import * as Control from "src/components/control-components"
|
import * as Control from "src/components/control-components"
|
||||||
import NiceIP from "src/components/nice-ip"
|
import NiceIP from "src/components/nice-ip"
|
||||||
@ -40,11 +40,13 @@ export default function DeviceDetailsView({
|
|||||||
{!readonly && (
|
{!readonly && (
|
||||||
<Button
|
<Button
|
||||||
sizeVariant="small"
|
sizeVariant="small"
|
||||||
onClick={() =>
|
onClick={() => {
|
||||||
|
// increment metrics before logout as we don't gracefully handle disconnect currently
|
||||||
|
incrementMetric("web_client_node_disconnect")
|
||||||
apiFetch("/local/v0/logout", "POST")
|
apiFetch("/local/v0/logout", "POST")
|
||||||
.then(() => setLocation("/"))
|
.then(() => setLocation("/"))
|
||||||
.catch((err) => alert("Logout failed: " + err.message))
|
.catch((err) => alert("Logout failed: " + err.message))
|
||||||
}
|
}}
|
||||||
>
|
>
|
||||||
Disconnect…
|
Disconnect…
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
import cx from "classnames"
|
import cx from "classnames"
|
||||||
import React, { useMemo } from "react"
|
import React, { useMemo } from "react"
|
||||||
|
import { incrementMetric } from "src/api"
|
||||||
import { ReactComponent as ArrowRight } from "src/assets/icons/arrow-right.svg"
|
import { ReactComponent as ArrowRight } from "src/assets/icons/arrow-right.svg"
|
||||||
import { ReactComponent as Machine } from "src/assets/icons/machine.svg"
|
import { ReactComponent as Machine } from "src/assets/icons/machine.svg"
|
||||||
import AddressCard from "src/components/address-copy-card"
|
import AddressCard from "src/components/address-copy-card"
|
||||||
@ -68,7 +69,11 @@ export default function HomeView({
|
|||||||
disabled={readonly}
|
disabled={readonly}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<Link className="link font-medium" to="/details">
|
<Link
|
||||||
|
className="link font-medium"
|
||||||
|
to="/details"
|
||||||
|
onClick={() => incrementMetric("web_client_device_details_click")}
|
||||||
|
>
|
||||||
View device details →
|
View device details →
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
// SPDX-License-Identifier: BSD-3-Clause
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
import React, { useCallback, useState } from "react"
|
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 { ReactComponent as TailscaleIcon } from "src/assets/icons/tailscale-icon.svg"
|
||||||
import { NodeData } from "src/hooks/node-data"
|
import { NodeData } from "src/hooks/node-data"
|
||||||
import Button from "src/ui/button"
|
import Button from "src/ui/button"
|
||||||
@ -25,7 +25,10 @@ export default function LoginView({
|
|||||||
|
|
||||||
const login = useCallback(
|
const login = useCallback(
|
||||||
(opt: TailscaleUpOptions) => {
|
(opt: TailscaleUpOptions) => {
|
||||||
tailscaleUp(opt).then(refreshData)
|
tailscaleUp(opt).then(() => {
|
||||||
|
incrementMetric("web_client_node_connect")
|
||||||
|
refreshData()
|
||||||
|
})
|
||||||
},
|
},
|
||||||
[refreshData]
|
[refreshData]
|
||||||
)
|
)
|
||||||
|
@ -148,9 +148,20 @@ export default function useNodeData() {
|
|||||||
setIsPosting(false)
|
setIsPosting(false)
|
||||||
mutate() // refresh data after PATCH finishes
|
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)
|
return apiFetch("/local/v0/prefs", "PATCH", d)
|
||||||
.then(onComplete)
|
.then(() => {
|
||||||
|
updateMetrics()
|
||||||
|
onComplete()
|
||||||
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
onComplete()
|
onComplete()
|
||||||
alert("Failed to update prefs")
|
alert("Failed to update prefs")
|
||||||
@ -176,6 +187,14 @@ export default function useNodeData() {
|
|||||||
: "web_client_advertise_exitnode_disable"
|
: "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)
|
return apiFetch("/routes", "POST", d)
|
||||||
@ -189,7 +208,7 @@ export default function useNodeData() {
|
|||||||
throw err
|
throw err
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
[mutate, data?.AdvertisingExitNode]
|
[mutate, data?.AdvertisingExitNode, data?.UsingExitNode?.ID]
|
||||||
)
|
)
|
||||||
|
|
||||||
const nodeUpdaters: NodeUpdaters = useMemo(
|
const nodeUpdaters: NodeUpdaters = useMemo(
|
||||||
@ -209,7 +228,7 @@ export default function useNodeData() {
|
|||||||
AdvertiseRoutes: routes,
|
AdvertiseRoutes: routes,
|
||||||
AdvertiseExitNode: data?.AdvertisingExitNode, // unchanged
|
AdvertiseExitNode: data?.AdvertisingExitNode, // unchanged
|
||||||
UseExitNode: data?.UsingExitNode?.ID, // unchanged
|
UseExitNode: data?.UsingExitNode?.ID, // unchanged
|
||||||
}),
|
}).then(() => incrementMetric("web_client_advertise_routes_change")),
|
||||||
}),
|
}),
|
||||||
[
|
[
|
||||||
data?.AdvertisingExitNode,
|
data?.AdvertisingExitNode,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user