From b762df179fbfff2e5dd0af5da1425bd306cefa3c Mon Sep 17 00:00:00 2001 From: Mike Sawka Date: Tue, 23 Jan 2024 17:19:03 -0800 Subject: [PATCH] zsh cleanup and stats (#247) * better osrelease parsing (ignore garbage at end of string) * add defaultshelltype to telemetry input * track reinit errors by shelltype to see if zsh integration is working --- wavesrv/cmd/main-server.go | 5 +--- wavesrv/pkg/cmdrunner/cmdrunner.go | 23 ++++------------ wavesrv/pkg/pcloud/pcloud.go | 4 ++- wavesrv/pkg/pcloud/pclouddata.go | 9 ++++--- wavesrv/pkg/remote/remote.go | 12 +++++++++ wavesrv/pkg/scbase/scbase.go | 13 ++++----- wavesrv/pkg/sstore/dbops.go | 13 ++++++++- wavesrv/pkg/sstore/sstore.go | 43 +++++++++++++++++------------- 8 files changed, 69 insertions(+), 53 deletions(-) diff --git a/wavesrv/cmd/main-server.go b/wavesrv/cmd/main-server.go index 5b5f8149a..716e13a5c 100644 --- a/wavesrv/cmd/main-server.go +++ b/wavesrv/cmd/main-server.go @@ -873,10 +873,7 @@ func main() { } log.Printf("PCLOUD_ENDPOINT=%s\n", pcloud.GetEndpoint()) - err = sstore.UpdateCurrentActivity(context.Background(), sstore.ActivityUpdate{NumConns: remote.NumRemotes()}) // set at least one record into activity - if err != nil { - log.Printf("[error] updating activity: %v\n", err) - } + sstore.UpdateActivityWrap(context.Background(), sstore.ActivityUpdate{NumConns: remote.NumRemotes()}, "numconns") // set at least one record into activity installSignalHandlers() go telemetryLoop() go stdinReadWatch() diff --git a/wavesrv/pkg/cmdrunner/cmdrunner.go b/wavesrv/pkg/cmdrunner/cmdrunner.go index e9cca8dd7..6da0a0b50 100644 --- a/wavesrv/pkg/cmdrunner/cmdrunner.go +++ b/wavesrv/pkg/cmdrunner/cmdrunner.go @@ -673,11 +673,7 @@ func EvalCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore. } evalDepth := getEvalDepth(ctx) if pk.Interactive && evalDepth == 0 { - err := sstore.UpdateCurrentActivity(ctx, sstore.ActivityUpdate{NumCommands: 1}) - if err != nil { - log.Printf("[error] incrementing activity numcommands: %v\n", err) - // fall through (non-fatal error) - } + sstore.UpdateActivityWrap(ctx, sstore.ActivityUpdate{NumCommands: 1}, "numcommands") } if evalDepth > MaxEvalDepth { return nil, fmt.Errorf("alias/history expansion max-depth exceeded") @@ -2753,10 +2749,7 @@ func validateRemoteColor(color string, typeStr string) error { func SessionOpenSharedCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) { activity := sstore.ActivityUpdate{ClickShared: 1} - err := sstore.UpdateCurrentActivity(ctx, activity) - if err != nil { - log.Printf("error updating click-shared: %v\n", err) - } + sstore.UpdateActivityWrap(ctx, activity, "click-shared") return nil, fmt.Errorf("shared sessions are not available in this version of prompt (stay tuned)") } @@ -3213,10 +3206,7 @@ func HistoryCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (ssto } show := !resolveBool(pk.Kwargs["noshow"], false) if show { - err = sstore.UpdateCurrentActivity(ctx, sstore.ActivityUpdate{HistoryView: 1}) - if err != nil { - log.Printf("error updating current activity (history): %v\n", err) - } + sstore.UpdateActivityWrap(ctx, sstore.ActivityUpdate{HistoryView: 1}, "history") } update := &sstore.ModelUpdate{} update.History = &sstore.HistoryInfoType{ @@ -3453,10 +3443,7 @@ func BookmarksShowCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) if err != nil { return nil, fmt.Errorf("cannot retrieve bookmarks: %v", err) } - err = sstore.UpdateCurrentActivity(ctx, sstore.ActivityUpdate{BookmarksView: 1}) - if err != nil { - log.Printf("error updating current activity (bookmarks): %v\n", err) - } + sstore.UpdateActivityWrap(ctx, sstore.ActivityUpdate{BookmarksView: 1}, "bookmarks") update := &sstore.ModelUpdate{ MainView: sstore.MainViewBookmarks, Bookmarks: bms, @@ -4504,7 +4491,7 @@ func ClientShowCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (s buf.WriteString(fmt.Sprintf(" %-15s %d\n", "db-version", dbVersion)) buf.WriteString(fmt.Sprintf(" %-15s %s\n", "client-version", clientVersion)) buf.WriteString(fmt.Sprintf(" %-15s %s %s\n", "server-version", scbase.WaveVersion, scbase.BuildTime)) - buf.WriteString(fmt.Sprintf(" %-15s %s (%s)\n", "arch", scbase.ClientArch(), scbase.MacOSRelease())) + buf.WriteString(fmt.Sprintf(" %-15s %s (%s)\n", "arch", scbase.ClientArch(), scbase.UnameKernelRelease())) update := &sstore.ModelUpdate{ Info: &sstore.InfoMsgType{ InfoTitle: fmt.Sprintf("client info"), diff --git a/wavesrv/pkg/pcloud/pcloud.go b/wavesrv/pkg/pcloud/pcloud.go index 1516da2d8..b7fc9edd0 100644 --- a/wavesrv/pkg/pcloud/pcloud.go +++ b/wavesrv/pkg/pcloud/pcloud.go @@ -18,6 +18,7 @@ import ( "sync" "time" + "github.com/wavetermdev/waveterm/waveshell/pkg/shellapi" "github.com/wavetermdev/waveterm/wavesrv/pkg/dbutil" "github.com/wavetermdev/waveterm/wavesrv/pkg/rtnstate" "github.com/wavetermdev/waveterm/wavesrv/pkg/scbase" @@ -164,7 +165,8 @@ func SendTelemetry(ctx context.Context, force bool) error { } log.Printf("[pcloud] sending telemetry data\n") dayStr := sstore.GetCurDayStr() - input := TelemetryInputType{UserId: clientData.UserId, ClientId: clientData.ClientId, CurDay: dayStr, Activity: activity} + defaultShellType := shellapi.DetectLocalShellType() + input := TelemetryInputType{UserId: clientData.UserId, ClientId: clientData.ClientId, CurDay: dayStr, DefaultShell: defaultShellType, Activity: activity} req, err := makeAnonPostReq(ctx, TelemetryUrl, input) if err != nil { return err diff --git a/wavesrv/pkg/pcloud/pclouddata.go b/wavesrv/pkg/pcloud/pclouddata.go index 04f00dfb5..0b4401c0b 100644 --- a/wavesrv/pkg/pcloud/pclouddata.go +++ b/wavesrv/pkg/pcloud/pclouddata.go @@ -19,10 +19,11 @@ type NoTelemetryInputType struct { } type TelemetryInputType struct { - UserId string `json:"userid"` - ClientId string `json:"clientid"` - CurDay string `json:"curday"` - Activity []*sstore.ActivityType `json:"activity"` + UserId string `json:"userid"` + ClientId string `json:"clientid"` + CurDay string `json:"curday"` + DefaultShell string `json:"defaultshell"` + Activity []*sstore.ActivityType `json:"activity"` } type WebShareUpdateType struct { diff --git a/wavesrv/pkg/remote/remote.go b/wavesrv/pkg/remote/remote.go index 9a4be7916..7add448cf 100644 --- a/wavesrv/pkg/remote/remote.go +++ b/wavesrv/pkg/remote/remote.go @@ -1103,6 +1103,16 @@ func getStateVarsFromInitPk(initPk *packet.InitPacketType) map[string]string { return rtn } +func makeReinitErrorUpdate(shellType string) sstore.ActivityUpdate { + rtn := sstore.ActivityUpdate{} + if shellType == packet.ShellType_bash { + rtn.ReinitBashErrors = 1 + } else if shellType == packet.ShellType_zsh { + rtn.ReinitZshErrors = 1 + } + return rtn +} + func (msh *MShellProc) ReInit(ctx context.Context, shellType string) (*packet.ShellStatePacketType, error) { if !msh.IsConnected() { return nil, fmt.Errorf("cannot reinit, remote is not connected") @@ -1122,12 +1132,14 @@ func (msh *MShellProc) ReInit(ctx context.Context, shellType string) (*packet.Sh } ssPk, ok := resp.(*packet.ShellStatePacketType) if !ok { + sstore.UpdateActivityWrap(ctx, makeReinitErrorUpdate(shellType), "reiniterror") if respPk, ok := resp.(*packet.ResponsePacketType); ok && respPk.Error != "" { return nil, fmt.Errorf("error reinitializing remote: %s", respPk.Error) } return nil, fmt.Errorf("invalid reinit response (not an shellstate packet): %T", resp) } if ssPk.State == nil { + sstore.UpdateActivityWrap(ctx, makeReinitErrorUpdate(shellType), "reiniterror") return nil, fmt.Errorf("invalid reinit response shellstate packet does not contain remote state") } // TODO: maybe we don't need to save statebase here. should be possible to save it on demand diff --git a/wavesrv/pkg/scbase/scbase.go b/wavesrv/pkg/scbase/scbase.go index be937e8aa..bcbcd8234 100644 --- a/wavesrv/pkg/scbase/scbase.go +++ b/wavesrv/pkg/scbase/scbase.go @@ -351,11 +351,11 @@ func ClientArch() string { return fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH) } -var releaseRegex = regexp.MustCompile(`^\d+\.\d+\.\d+$`) +var releaseRegex = regexp.MustCompile(`^(\d+\.\d+\.\d+)`) var osReleaseOnce = &sync.Once{} var osRelease string -func macOSRelease() string { +func unameKernelRelease() string { ctx, cancelFn := context.WithTimeout(context.Background(), 2*time.Second) defer cancelFn() out, err := exec.CommandContext(ctx, "uname", "-r").CombinedOutput() @@ -364,16 +364,17 @@ func macOSRelease() string { return "-" } releaseStr := strings.TrimSpace(string(out)) - if !releaseRegex.MatchString(releaseStr) { + m := releaseRegex.FindStringSubmatch(releaseStr) + if m == nil || len(m) < 2 { log.Printf("invalid uname -r output: [%s]\n", releaseStr) return "-" } - return releaseStr + return m[1] } -func MacOSRelease() string { +func UnameKernelRelease() string { osReleaseOnce.Do(func() { - osRelease = macOSRelease() + osRelease = unameKernelRelease() }) return osRelease } diff --git a/wavesrv/pkg/sstore/dbops.go b/wavesrv/pkg/sstore/dbops.go index cdef6b921..9f4085851 100644 --- a/wavesrv/pkg/sstore/dbops.go +++ b/wavesrv/pkg/sstore/dbops.go @@ -2157,6 +2157,15 @@ func GetCurDayStr() string { return dayStr } +// Wraps UpdateCurrentActivity, but ignores errors +func UpdateActivityWrap(ctx context.Context, update ActivityUpdate, debugStr string) { + err := UpdateCurrentActivity(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 UpdateCurrentActivity(ctx context.Context, update ActivityUpdate) error { now := time.Now() dayStr := GetCurDayStr() @@ -2171,7 +2180,7 @@ func UpdateCurrentActivity(ctx context.Context, update ActivityUpdate) error { if len(tzName) > MaxTzNameLen { tzName = tzName[0:MaxTzNameLen] } - tx.Exec(query, dayStr, tdata, tzName, tzOffset, scbase.WaveVersion, scbase.ClientArch(), scbase.BuildTime, scbase.MacOSRelease()) + tx.Exec(query, dayStr, tdata, tzName, tzOffset, scbase.WaveVersion, scbase.ClientArch(), scbase.BuildTime, scbase.UnameKernelRelease()) } tdata.NumCommands += update.NumCommands tdata.FgMinutes += update.FgMinutes @@ -2180,6 +2189,8 @@ func UpdateCurrentActivity(ctx context.Context, update ActivityUpdate) error { tdata.ClickShared += update.ClickShared tdata.HistoryView += update.HistoryView tdata.BookmarksView += update.BookmarksView + tdata.ReinitBashErrors += update.ReinitBashErrors + tdata.ReinitZshErrors += update.ReinitZshErrors if update.NumConns > 0 { tdata.NumConns = update.NumConns } diff --git a/wavesrv/pkg/sstore/sstore.go b/wavesrv/pkg/sstore/sstore.go index cfa60036f..48bf9e4cf 100644 --- a/wavesrv/pkg/sstore/sstore.go +++ b/wavesrv/pkg/sstore/sstore.go @@ -226,16 +226,18 @@ type ClientWinSizeType struct { } type ActivityUpdate struct { - FgMinutes int - ActiveMinutes int - OpenMinutes int - NumCommands int - ClickShared int - HistoryView int - BookmarksView int - NumConns int - WebShareLimit int - BuildTime string + FgMinutes int + ActiveMinutes int + OpenMinutes int + NumCommands int + ClickShared int + HistoryView int + BookmarksView int + NumConns int + WebShareLimit int + ReinitBashErrors int + ReinitZshErrors int + BuildTime string } type ActivityType struct { @@ -247,19 +249,22 @@ type ActivityType struct { ClientVersion string `json:"clientversion"` ClientArch string `json:"clientarch"` BuildTime string `json:"buildtime"` + DefaultShell string `json:"defaultshell"` OSRelease string `json:"osrelease"` } type TelemetryData struct { - NumCommands int `json:"numcommands"` - ActiveMinutes int `json:"activeminutes"` - FgMinutes int `json:"fgminutes"` - OpenMinutes int `json:"openminutes"` - ClickShared int `json:"clickshared,omitempty"` - HistoryView int `json:"historyview,omitempty"` - BookmarksView int `json:"bookmarksview,omitempty"` - NumConns int `json:"numconns"` - WebShareLimit int `json:"websharelimit,omitempty"` + NumCommands int `json:"numcommands"` + ActiveMinutes int `json:"activeminutes"` + FgMinutes int `json:"fgminutes"` + OpenMinutes int `json:"openminutes"` + ClickShared int `json:"clickshared,omitempty"` + HistoryView int `json:"historyview,omitempty"` + BookmarksView int `json:"bookmarksview,omitempty"` + NumConns int `json:"numconns"` + WebShareLimit int `json:"websharelimit,omitempty"` + ReinitBashErrors int `json:"reinitbasherrors,omitempty"` + ReinitZshErrors int `json:"reinitzsherrors,omitempty"` } func (tdata TelemetryData) Value() (driver.Value, error) {