mirror of
https://github.com/wavetermdev/waveterm.git
synced 2024-12-22 16:48:23 +01:00
215 lines
7.1 KiB
Go
215 lines
7.1 KiB
Go
// Copyright 2024, Command Line Inc.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package telemetry
|
|
|
|
import (
|
|
"context"
|
|
"database/sql/driver"
|
|
"log"
|
|
"time"
|
|
|
|
"github.com/wavetermdev/waveterm/pkg/panichandler"
|
|
"github.com/wavetermdev/waveterm/pkg/util/daystr"
|
|
"github.com/wavetermdev/waveterm/pkg/util/dbutil"
|
|
"github.com/wavetermdev/waveterm/pkg/wavebase"
|
|
"github.com/wavetermdev/waveterm/pkg/wconfig"
|
|
"github.com/wavetermdev/waveterm/pkg/wshrpc"
|
|
"github.com/wavetermdev/waveterm/pkg/wstore"
|
|
)
|
|
|
|
const MaxTzNameLen = 50
|
|
|
|
type ActivityType struct {
|
|
Day string `json:"day"`
|
|
Uploaded bool `json:"-"`
|
|
TData TelemetryData `json:"tdata"`
|
|
TzName string `json:"tzname"`
|
|
TzOffset int `json:"tzoffset"`
|
|
ClientVersion string `json:"clientversion"`
|
|
ClientArch string `json:"clientarch"`
|
|
BuildTime string `json:"buildtime"`
|
|
OSRelease string `json:"osrelease"`
|
|
}
|
|
|
|
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"`
|
|
NumWS int `json:"numws,omitempty"`
|
|
NumWSNamed int `json:"numwsnamed,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"`
|
|
NumPanics int `json:"numpanics,omitempty"`
|
|
NumAIReqs int `json:"numaireqs,omitempty"`
|
|
SetTabTheme int `json:"settabtheme,omitempty"`
|
|
Displays []wshrpc.ActivityDisplayType `json:"displays,omitempty"`
|
|
Renderers map[string]int `json:"renderers,omitempty"`
|
|
Blocks map[string]int `json:"blocks,omitempty"`
|
|
WshCmds map[string]int `json:"wshcmds,omitempty"`
|
|
Conn map[string]int `json:"conn,omitempty"`
|
|
}
|
|
|
|
func (tdata TelemetryData) Value() (driver.Value, error) {
|
|
return dbutil.QuickValueJson(tdata)
|
|
}
|
|
|
|
func (tdata *TelemetryData) Scan(val interface{}) error {
|
|
return dbutil.QuickScanJson(tdata, val)
|
|
}
|
|
|
|
func IsTelemetryEnabled() bool {
|
|
settings := wconfig.GetWatcher().GetFullConfig()
|
|
return settings.Settings.TelemetryEnabled
|
|
}
|
|
|
|
func IsAutoUpdateEnabled() bool {
|
|
settings := wconfig.GetWatcher().GetFullConfig()
|
|
return settings.Settings.AutoUpdateEnabled
|
|
}
|
|
|
|
func AutoUpdateChannel() string {
|
|
settings := wconfig.GetWatcher().GetFullConfig()
|
|
return settings.Settings.AutoUpdateChannel
|
|
}
|
|
|
|
// Wraps UpdateCurrentActivity, spawns goroutine, and logs errors
|
|
func GoUpdateActivityWrap(update wshrpc.ActivityUpdate, debugStr string) {
|
|
go func() {
|
|
defer panichandler.PanicHandlerNoTelemetry("GoUpdateActivityWrap")
|
|
ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancelFn()
|
|
err := UpdateActivity(ctx, update)
|
|
if err != nil {
|
|
// ignore error, just log, since this is not critical
|
|
log.Printf("error updating current activity (%s): %v\n", debugStr, err)
|
|
}
|
|
}()
|
|
}
|
|
|
|
func UpdateActivity(ctx context.Context, update wshrpc.ActivityUpdate) error {
|
|
now := time.Now()
|
|
dayStr := daystr.GetCurDayStr()
|
|
txErr := wstore.WithTx(ctx, func(tx *wstore.TxWrap) error {
|
|
var tdata TelemetryData
|
|
query := `SELECT tdata FROM db_activity WHERE day = ?`
|
|
found := tx.Get(&tdata, query, dayStr)
|
|
if !found {
|
|
query = `INSERT INTO db_activity (day, uploaded, tdata, tzname, tzoffset, clientversion, clientarch, buildtime, osrelease)
|
|
VALUES ( ?, 0, ?, ?, ?, ?, ?, ?, ?)`
|
|
tzName, tzOffset := now.Zone()
|
|
if len(tzName) > MaxTzNameLen {
|
|
tzName = tzName[0:MaxTzNameLen]
|
|
}
|
|
tx.Exec(query, dayStr, tdata, tzName, tzOffset, wavebase.WaveVersion, wavebase.ClientArch(), wavebase.BuildTime, wavebase.UnameKernelRelease())
|
|
}
|
|
tdata.FgMinutes += update.FgMinutes
|
|
tdata.ActiveMinutes += update.ActiveMinutes
|
|
tdata.OpenMinutes += update.OpenMinutes
|
|
tdata.NewTab += update.NewTab
|
|
tdata.NumStartup += update.Startup
|
|
tdata.NumShutdown += update.Shutdown
|
|
tdata.SetTabTheme += update.SetTabTheme
|
|
tdata.NumMagnify += update.NumMagnify
|
|
tdata.NumPanics += update.NumPanics
|
|
tdata.NumAIReqs += update.NumAIReqs
|
|
if update.NumTabs > 0 {
|
|
tdata.NumTabs = update.NumTabs
|
|
}
|
|
if update.NumBlocks > 0 {
|
|
tdata.NumBlocks = update.NumBlocks
|
|
}
|
|
if update.NumWindows > 0 {
|
|
tdata.NumWindows = update.NumWindows
|
|
}
|
|
if update.NumWS > 0 {
|
|
tdata.NumWS = update.NumWS
|
|
}
|
|
if update.NumWSNamed > 0 {
|
|
tdata.NumWSNamed = update.NumWSNamed
|
|
}
|
|
if update.NumSSHConn > 0 && update.NumSSHConn > tdata.NumSSHConn {
|
|
tdata.NumSSHConn = update.NumSSHConn
|
|
}
|
|
if update.NumWSLConn > 0 && update.NumWSLConn > tdata.NumWSLConn {
|
|
tdata.NumWSLConn = update.NumWSLConn
|
|
}
|
|
if len(update.Renderers) > 0 {
|
|
if tdata.Renderers == nil {
|
|
tdata.Renderers = make(map[string]int)
|
|
}
|
|
for key, val := range update.Renderers {
|
|
tdata.Renderers[key] += val
|
|
}
|
|
}
|
|
if len(update.WshCmds) > 0 {
|
|
if tdata.WshCmds == nil {
|
|
tdata.WshCmds = make(map[string]int)
|
|
}
|
|
for key, val := range update.WshCmds {
|
|
tdata.WshCmds[key] += val
|
|
}
|
|
}
|
|
if len(update.Conn) > 0 {
|
|
if tdata.Conn == nil {
|
|
tdata.Conn = make(map[string]int)
|
|
}
|
|
for key, val := range update.Conn {
|
|
tdata.Conn[key] += val
|
|
}
|
|
}
|
|
if len(update.Displays) > 0 {
|
|
tdata.Displays = update.Displays
|
|
}
|
|
if len(update.Blocks) > 0 {
|
|
tdata.Blocks = update.Blocks
|
|
}
|
|
query = `UPDATE db_activity
|
|
SET tdata = ?,
|
|
clientversion = ?,
|
|
buildtime = ?
|
|
WHERE day = ?`
|
|
tx.Exec(query, tdata, wavebase.WaveVersion, wavebase.BuildTime, dayStr)
|
|
return nil
|
|
})
|
|
if txErr != nil {
|
|
return txErr
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func GetNonUploadedActivity(ctx context.Context) ([]*ActivityType, error) {
|
|
var rtn []*ActivityType
|
|
txErr := wstore.WithTx(ctx, func(tx *wstore.TxWrap) error {
|
|
query := `SELECT * FROM db_activity WHERE uploaded = 0 ORDER BY day DESC LIMIT 30`
|
|
tx.Select(&rtn, query)
|
|
return nil
|
|
})
|
|
if txErr != nil {
|
|
return nil, txErr
|
|
}
|
|
return rtn, nil
|
|
}
|
|
|
|
func MarkActivityAsUploaded(ctx context.Context, activityArr []*ActivityType) error {
|
|
dayStr := daystr.GetCurDayStr()
|
|
txErr := wstore.WithTx(ctx, func(tx *wstore.TxWrap) error {
|
|
query := `UPDATE db_activity SET uploaded = 1 WHERE day = ?`
|
|
for _, activity := range activityArr {
|
|
if activity.Day == dayStr {
|
|
continue
|
|
}
|
|
tx.Exec(query, activity.Day)
|
|
}
|
|
return nil
|
|
})
|
|
return txErr
|
|
}
|