From 24807cfd34a26766026025626503eb99d1282067 Mon Sep 17 00:00:00 2001 From: Mike Sawka Date: Tue, 19 Nov 2024 19:41:53 -0800 Subject: [PATCH] add display resolutions to activity updates, update docs (#1323) --- docs/docs/telemetry.mdx | 10 +++++ emain/emain.ts | 39 +++++++++++++---- frontend/types/gotypes.d.ts | 9 ++++ pkg/telemetry/telemetry.go | 86 ++++++++++++++++++------------------- pkg/web/web.go | 32 -------------- 5 files changed, 93 insertions(+), 83 deletions(-) diff --git a/docs/docs/telemetry.mdx b/docs/docs/telemetry.mdx index 1ece38cb1..a9a21b75e 100644 --- a/docs/docs/telemetry.mdx +++ b/docs/docs/telemetry.mdx @@ -47,11 +47,20 @@ When telemetry is active, we collect the following data. It is stored in the `te | ActiveMinutes | The number of minutes that the user has actively used Waveterm on a given day. This requires the terminal window to be in focus while the user is actively interacting with it. | | FgMinutes | The number of minutes that Waveterm has been in the foreground on a given day. This requires the terminal window to be in focus regardless of user interaction. | | OpenMinutes | The number of minutes that Waveterm has been open on a given day. This only requires that the terminal is open, even if the window is out of focus. | +| NumBlocks | The number of existing blocks open on a given day | | NumTabs | The number of existing tabs open on a given day. | +| NewTab | The number of new tabs created on a given day | +| NumWindows | The number of existing windows open on a give day. | | NewTab | The number of new tabs opened on a given day. | | NumStartup | The number of times waveterm has been started on a given day. | | NumShutdown | The number of times waveterm has been shut down on a given day. | +| SetTabTheme | The number of times the tab theme is changed from the context menu | +| NumMagnify | The number of times any block is magnified | +| NumSSHConn | The number of distinct SSH connections that have been made to distinct hosts | +| NumWSLConns | The number of distinct WSL connections that have been made to distinct distros | | Renderers | The number of new block views of each type are open on a given day. | +| WshCmds | The number of wsh commands of each type run on a given day | +| Conn | The number of successful remote connections made (and errors) on a given day | ## Associated Data @@ -67,6 +76,7 @@ In addition to the telemetry data collected, the following is also reported. It | ClientArch | This includes the user's operating system (e.g. linux or darwin) and architecture (e.g. x86_64 or arm64). It does not include data for any Connections at this time. | | BuildTime | This serves as a more accurate version number that keeps track of when we built the version. It has no bearing on when that version was installed by you. | | OSRelease | This lists the version of the operating system the user has installed. | +| Displays | Display resolutions (added in v0.9.3 to help us understand what screen resolutions to optimize for) | ## Telemetry Metadata diff --git a/emain/emain.ts b/emain/emain.ts index ee8b9650f..c5ad4f55f 100644 --- a/emain/emain.ts +++ b/emain/emain.ts @@ -1,6 +1,7 @@ // Copyright 2024, Command Line Inc. // SPDX-License-Identifier: Apache-2.0 +import { RpcApi } from "@/app/store/wshclientapi"; import * as electron from "electron"; import { FastAverageColor } from "fast-average-color"; import fs from "fs"; @@ -14,7 +15,6 @@ import winston from "winston"; import * as services from "../frontend/app/store/services"; import { initElectronWshrpc, shutdownWshrpc } from "../frontend/app/store/wshrpcutil"; import { getWebServerEndpoint } from "../frontend/util/endpoints"; -import { fetch } from "../frontend/util/fetchutil"; import * as keyutil from "../frontend/util/keyutil"; import { fireAndForget } from "../frontend/util/util"; import { AuthKey, configureAuthKeyRequestInjection } from "./authkey"; @@ -518,16 +518,39 @@ electron.ipcMain.on("contextmenu-show", (event, menuDefArr?: ElectronContextMenu event.returnValue = true; }); +// we try to set the primary display as index [0] +function getActivityDisplays(): ActivityDisplayType[] { + const displays = electron.screen.getAllDisplays(); + const primaryDisplay = electron.screen.getPrimaryDisplay(); + const rtn: ActivityDisplayType[] = []; + for (const display of displays) { + const adt = { + width: display.size.width, + height: display.size.height, + dpr: display.scaleFactor, + internal: display.internal, + }; + if (display.id === primaryDisplay?.id) { + rtn.unshift(adt); + } else { + rtn.push(adt); + } + } + return rtn; +} + async function logActiveState() { const astate = getActivityState(); - const activeState = { fg: astate.wasInFg, active: astate.wasActive, open: true }; - const url = new URL(getWebServerEndpoint() + "/wave/log-active-state"); + const activity: ActivityUpdate = { openminutes: 1 }; + if (astate.wasInFg) { + activity.fgminutes = 1; + } + if (astate.wasActive) { + activity.activeminutes = 1; + } + activity.displays = getActivityDisplays(); try { - const resp = await fetch(url, { method: "post", body: JSON.stringify(activeState) }); - if (!resp.ok) { - console.log("error logging active state", resp.status, resp.statusText); - return; - } + RpcApi.ActivityCommand(ElectronWshClient, activity, { noresponse: true }); } catch (e) { console.log("error logging active state", e); } finally { diff --git a/frontend/types/gotypes.d.ts b/frontend/types/gotypes.d.ts index 8153127c6..b01a7d1bb 100644 --- a/frontend/types/gotypes.d.ts +++ b/frontend/types/gotypes.d.ts @@ -5,6 +5,14 @@ declare global { + // telemetry.ActivityDisplayType + type ActivityDisplayType = { + width: number; + height: number; + dpr: number; + internal?: boolean; + }; + // telemetry.ActivityUpdate type ActivityUpdate = { fgminutes?: number; @@ -21,6 +29,7 @@ declare global { shutdown?: number; settabtheme?: number; buildtime?: string; + displays?: ActivityDisplayType[]; renderers?: {[key: string]: number}; wshcmds?: {[key: string]: number}; conn?: {[key: string]: number}; diff --git a/pkg/telemetry/telemetry.go b/pkg/telemetry/telemetry.go index b586a8a82..0ba5ded7a 100644 --- a/pkg/telemetry/telemetry.go +++ b/pkg/telemetry/telemetry.go @@ -18,36 +18,32 @@ import ( const MaxTzNameLen = 50 -// "terminal" should not be in this list -var allowedRenderers = map[string]bool{ - "markdown": true, - "code": true, - "openai": true, - "csv": true, - "image": true, - "pdf": true, - "media": true, - "mustache": true, +type ActivityDisplayType struct { + Width int `json:"width"` + Height int `json:"height"` + DPR float64 `json:"dpr"` + Internal bool `json:"internal,omitempty"` } type ActivityUpdate struct { - FgMinutes int `json:"fgminutes,omitempty"` - ActiveMinutes int `json:"activeminutes,omitempty"` - OpenMinutes int `json:"openminutes,omitempty"` - NumTabs int `json:"numtabs,omitempty"` - NewTab int `json:"newtab,omitempty"` - NumBlocks int `json:"numblocks,omitempty"` - NumWindows int `json:"numwindows,omitempty"` - NumSSHConn int `json:"numsshconn,omitempty"` - NumWSLConn int `json:"numwslconn,omitempty"` - NumMagnify int `json:"nummagnify,omitempty"` - Startup int `json:"startup,omitempty"` - Shutdown int `json:"shutdown,omitempty"` - SetTabTheme int `json:"settabtheme,omitempty"` - BuildTime string `json:"buildtime,omitempty"` - Renderers map[string]int `json:"renderers,omitempty"` - WshCmds map[string]int `json:"wshcmds,omitempty"` - Conn map[string]int `json:"conn,omitempty"` + FgMinutes int `json:"fgminutes,omitempty"` + ActiveMinutes int `json:"activeminutes,omitempty"` + OpenMinutes int `json:"openminutes,omitempty"` + NumTabs int `json:"numtabs,omitempty"` + NewTab int `json:"newtab,omitempty"` + NumBlocks int `json:"numblocks,omitempty"` + NumWindows int `json:"numwindows,omitempty"` + NumSSHConn int `json:"numsshconn,omitempty"` + NumWSLConn int `json:"numwslconn,omitempty"` + NumMagnify int `json:"nummagnify,omitempty"` + Startup int `json:"startup,omitempty"` + Shutdown int `json:"shutdown,omitempty"` + SetTabTheme int `json:"settabtheme,omitempty"` + BuildTime string `json:"buildtime,omitempty"` + Displays []ActivityDisplayType `json:"displays,omitempty"` + Renderers map[string]int `json:"renderers,omitempty"` + WshCmds map[string]int `json:"wshcmds,omitempty"` + Conn map[string]int `json:"conn,omitempty"` } type ActivityType struct { @@ -63,22 +59,23 @@ type ActivityType struct { } type TelemetryData struct { - ActiveMinutes int `json:"activeminutes"` - FgMinutes int `json:"fgminutes"` - OpenMinutes int `json:"openminutes"` - NumTabs int `json:"numtabs"` - NumBlocks int `json:"numblocks,omitempty"` - NumWindows int `json:"numwindows,omitempty"` - NumSSHConn int `json:"numsshconn,omitempty"` - NumWSLConn int `json:"numwslconn,omitempty"` - NumMagnify int `json:"nummagnify,omitempty"` - NewTab int `json:"newtab"` - NumStartup int `json:"numstartup,omitempty"` - NumShutdown int `json:"numshutdown,omitempty"` - SetTabTheme int `json:"settabtheme,omitempty"` - Renderers map[string]int `json:"renderers,omitempty"` - WshCmds map[string]int `json:"wshcmds,omitempty"` - Conn map[string]int `json:"conn,omitempty"` + ActiveMinutes int `json:"activeminutes"` + FgMinutes int `json:"fgminutes"` + OpenMinutes int `json:"openminutes"` + NumTabs int `json:"numtabs"` + NumBlocks int `json:"numblocks,omitempty"` + NumWindows int `json:"numwindows,omitempty"` + NumSSHConn int `json:"numsshconn,omitempty"` + NumWSLConn int `json:"numwslconn,omitempty"` + NumMagnify int `json:"nummagnify,omitempty"` + NewTab int `json:"newtab"` + NumStartup int `json:"numstartup,omitempty"` + NumShutdown int `json:"numshutdown,omitempty"` + SetTabTheme int `json:"settabtheme,omitempty"` + Displays []ActivityDisplayType `json:"displays,omitempty"` + Renderers map[string]int `json:"renderers,omitempty"` + WshCmds map[string]int `json:"wshcmds,omitempty"` + Conn map[string]int `json:"conn,omitempty"` } func (tdata TelemetryData) Value() (driver.Value, error) { @@ -180,6 +177,9 @@ func UpdateActivity(ctx context.Context, update ActivityUpdate) error { tdata.Conn[key] += val } } + if len(update.Displays) > 0 { + tdata.Displays = update.Displays + } query = `UPDATE db_activity SET tdata = ?, clientversion = ?, diff --git a/pkg/web/web.go b/pkg/web/web.go index 85c17df51..ebe967482 100644 --- a/pkg/web/web.go +++ b/pkg/web/web.go @@ -26,14 +26,11 @@ import ( "github.com/wavetermdev/waveterm/pkg/docsite" "github.com/wavetermdev/waveterm/pkg/filestore" "github.com/wavetermdev/waveterm/pkg/service" - "github.com/wavetermdev/waveterm/pkg/telemetry" "github.com/wavetermdev/waveterm/pkg/wavebase" - "github.com/wavetermdev/waveterm/pkg/waveobj" "github.com/wavetermdev/waveterm/pkg/wshrpc" "github.com/wavetermdev/waveterm/pkg/wshrpc/wshclient" "github.com/wavetermdev/waveterm/pkg/wshrpc/wshserver" "github.com/wavetermdev/waveterm/pkg/wshutil" - "github.com/wavetermdev/waveterm/pkg/wstore" ) type WebFnType = func(http.ResponseWriter, *http.Request) @@ -358,34 +355,6 @@ type ClientActiveState struct { Open bool `json:"open"` } -// params: fg, active, open -func handleLogActiveState(w http.ResponseWriter, r *http.Request) { - decoder := json.NewDecoder(r.Body) - var activeState ClientActiveState - err := decoder.Decode(&activeState) - if err != nil { - WriteJsonError(w, fmt.Errorf("error decoding json: %v", err)) - return - } - activity := telemetry.ActivityUpdate{} - if activeState.Fg { - activity.FgMinutes = 1 - } - if activeState.Active { - activity.ActiveMinutes = 1 - } - if activeState.Open { - activity.OpenMinutes = 1 - } - activity.NumTabs, _ = wstore.DBGetCount[*waveobj.Tab](r.Context()) - err = telemetry.UpdateActivity(r.Context(), activity) - if err != nil { - WriteJsonError(w, fmt.Errorf("error updating activity: %w", err)) - return - } - WriteJsonSuccess(w, true) -} - func WebFnWrap(opts WebFnOpts, fn WebFnType) WebFnType { return func(w http.ResponseWriter, r *http.Request) { defer func() { @@ -450,7 +419,6 @@ func RunWebServer(listener net.Listener) { gr.HandleFunc("/wave/stream-file", WebFnWrap(WebFnOpts{AllowCaching: true}, handleStreamFile)) gr.HandleFunc("/wave/file", WebFnWrap(WebFnOpts{AllowCaching: false}, handleWaveFile)) gr.HandleFunc("/wave/service", WebFnWrap(WebFnOpts{JsonErrors: true}, handleService)) - gr.HandleFunc("/wave/log-active-state", WebFnWrap(WebFnOpts{JsonErrors: true}, handleLogActiveState)) gr.HandleFunc("/vdom/{uuid}/{path:.*}", WebFnWrap(WebFnOpts{AllowCaching: true}, handleVDom)) gr.PathPrefix(docsitePrefix).Handler(http.StripPrefix(docsitePrefix, docsite.GetDocsiteHandler())) handler := http.TimeoutHandler(gr, HttpTimeoutDuration, "Timeout")