Skip to content

Commit

Permalink
Merge pull request #2750 from ActiveState/mitchell/dx-2145
Browse files Browse the repository at this point in the history
Add source to analytics events.
  • Loading branch information
mitchell-as authored Sep 15, 2023
2 parents 5731dea + bfef23d commit 1335957
Show file tree
Hide file tree
Showing 32 changed files with 229 additions and 156 deletions.
44 changes: 21 additions & 23 deletions cmd/state-installer/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (

"github.com/ActiveState/cli/internal/analytics"
"github.com/ActiveState/cli/internal/analytics/client/sync"
anaConst "github.com/ActiveState/cli/internal/analytics/constants"
"github.com/ActiveState/cli/internal/captain"
"github.com/ActiveState/cli/internal/config"
"github.com/ActiveState/cli/internal/constants"
Expand All @@ -37,9 +38,6 @@ import (
"golang.org/x/crypto/ssh/terminal"
)

const AnalyticsCat = "installer"
const AnalyticsFunnelCat = "installer-funnel"

type Params struct {
sourceInstaller string
path string
Expand Down Expand Up @@ -125,8 +123,8 @@ func main() {
logging.Debug("Original Args: %v", os.Args)
logging.Debug("Processed Args: %v", processedArgs)

an = sync.New(cfg, nil, out)
an.Event(AnalyticsFunnelCat, "start")
an = sync.New(anaConst.SrcStateInstaller, cfg, nil, out)
an.Event(anaConst.CatInstallerFunnel, "start")

params := newParams()
cmd := captain.NewCommand(
Expand Down Expand Up @@ -193,30 +191,30 @@ func main() {
},
)

an.Event(AnalyticsFunnelCat, "pre-exec")
an.Event(anaConst.CatInstallerFunnel, "pre-exec")
err = cmd.Execute(processedArgs[1:])
if err != nil {
errors.ReportError(err, cmd, an)
if locale.IsInputError(err) {
an.EventWithLabel(AnalyticsCat, "input-error", errs.JoinMessage(err))
an.EventWithLabel(anaConst.CatInstaller, "input-error", errs.JoinMessage(err))
logging.Debug("Installer input error: " + errs.JoinMessage(err))
} else {
an.EventWithLabel(AnalyticsCat, "error", errs.JoinMessage(err))
an.EventWithLabel(anaConst.CatInstaller, "error", errs.JoinMessage(err))
multilog.Critical("Installer error: " + errs.JoinMessage(err))
}

an.EventWithLabel(AnalyticsFunnelCat, "fail", errs.JoinMessage(err))
an.EventWithLabel(anaConst.CatInstallerFunnel, "fail", errs.JoinMessage(err))
exitCode, err = errors.ParseUserFacing(err)
if err != nil {
out.Error(err)
}
} else {
an.Event(AnalyticsFunnelCat, "success")
an.Event(anaConst.CatInstallerFunnel, "success")
}
}

func execute(out output.Outputer, cfg *config.Instance, an analytics.Dispatcher, args []string, params *Params) error {
an.Event(AnalyticsFunnelCat, "exec")
an.Event(anaConst.CatInstallerFunnel, "exec")

if params.path == "" {
var err error
Expand Down Expand Up @@ -274,13 +272,13 @@ func execute(out output.Outputer, cfg *config.Instance, an analytics.Dispatcher,
if params.isUpdate {
route = "update"
}
an.Event(AnalyticsFunnelCat, route)
an.Event(anaConst.CatInstallerFunnel, route)

// Check if state tool already installed
if !params.isUpdate && !params.force && stateToolInstalled && !targetingSameBranch {
logging.Debug("Cancelling out because State Tool is already installed")
out.Print(fmt.Sprintf("State Tool Package Manager is already installed at [NOTICE]%s[/RESET]. To reinstall use the [ACTIONABLE]--force[/RESET] flag.", installPath))
an.Event(AnalyticsFunnelCat, "already-installed")
an.Event(anaConst.CatInstallerFunnel, "already-installed")
params.isUpdate = true
return postInstallEvents(out, cfg, an, params)
}
Expand All @@ -295,7 +293,7 @@ func execute(out output.Outputer, cfg *config.Instance, an analytics.Dispatcher,
// installOrUpdateFromLocalSource is invoked when we're performing an installation where the payload is already provided
func installOrUpdateFromLocalSource(out output.Outputer, cfg *config.Instance, an analytics.Dispatcher, payloadPath string, params *Params) error {
logging.Debug("Install from local source")
an.Event(AnalyticsFunnelCat, "local-source")
an.Event(anaConst.CatInstallerFunnel, "local-source")
if !params.isUpdate {
// install.sh or install.ps1 downloaded this installer and is running it.
out.Print(output.Title("Installing State Tool Package Manager"))
Expand Down Expand Up @@ -324,12 +322,12 @@ func installOrUpdateFromLocalSource(out output.Outputer, cfg *config.Instance, a
}

// Run installer
an.Event(AnalyticsFunnelCat, "pre-installer")
an.Event(anaConst.CatInstallerFunnel, "pre-installer")
if err := installer.Install(); err != nil {
out.Print("[ERROR]x Failed[/RESET]")
return err
}
an.Event(AnalyticsFunnelCat, "post-installer")
an.Event(anaConst.CatInstallerFunnel, "post-installer")
out.Print("[SUCCESS]✔ Done[/RESET]")

if !params.isUpdate {
Expand All @@ -342,7 +340,7 @@ func installOrUpdateFromLocalSource(out output.Outputer, cfg *config.Instance, a
}

func postInstallEvents(out output.Outputer, cfg *config.Instance, an analytics.Dispatcher, params *Params) error {
an.Event(AnalyticsFunnelCat, "post-install-events")
an.Event(anaConst.CatInstallerFunnel, "post-install-events")

installPath, err := resolveInstallPath(params.path)
if err != nil {
Expand All @@ -368,30 +366,30 @@ func postInstallEvents(out output.Outputer, cfg *config.Instance, an analytics.D
switch {
// Execute provided --command
case params.command != "":
an.Event(AnalyticsFunnelCat, "forward-command")
an.Event(anaConst.CatInstallerFunnel, "forward-command")

out.Print(fmt.Sprintf("\nRunning `[ACTIONABLE]%s[/RESET]`\n", params.command))
cmd, args := exeutils.DecodeCmd(params.command)
if _, _, err := exeutils.ExecuteAndPipeStd(cmd, args, envSlice(binPath)); err != nil {
an.EventWithLabel(AnalyticsFunnelCat, "forward-command-err", err.Error())
an.EventWithLabel(anaConst.CatInstallerFunnel, "forward-command-err", err.Error())
return errs.Silence(errs.Wrap(err, "Running provided command failed, error returned: %s", errs.JoinMessage(err)))
}
// Activate provided --activate Namespace
case params.activate.IsValid():
an.Event(AnalyticsFunnelCat, "forward-activate")
an.Event(anaConst.CatInstallerFunnel, "forward-activate")

out.Print(fmt.Sprintf("\nRunning `[ACTIONABLE]state activate %s[/RESET]`\n", params.activate.String()))
if _, _, err := exeutils.ExecuteAndPipeStd(stateExe, []string{"activate", params.activate.String()}, envSlice(binPath)); err != nil {
an.EventWithLabel(AnalyticsFunnelCat, "forward-activate-err", err.Error())
an.EventWithLabel(anaConst.CatInstallerFunnel, "forward-activate-err", err.Error())
return errs.Silence(errs.Wrap(err, "Could not activate %s, error returned: %s", params.activate.String(), errs.JoinMessage(err)))
}
// Activate provided --activate-default Namespace
case params.activateDefault.IsValid():
an.Event(AnalyticsFunnelCat, "forward-activate-default")
an.Event(anaConst.CatInstallerFunnel, "forward-activate-default")

out.Print(fmt.Sprintf("\nRunning `[ACTIONABLE]state activate --default %s[/RESET]`\n", params.activateDefault.String()))
if _, _, err := exeutils.ExecuteAndPipeStd(stateExe, []string{"activate", params.activateDefault.String(), "--default"}, envSlice(binPath)); err != nil {
an.EventWithLabel(AnalyticsFunnelCat, "forward-activate-default-err", err.Error())
an.EventWithLabel(anaConst.CatInstallerFunnel, "forward-activate-default-err", err.Error())
return errs.Silence(errs.Wrap(err, "Could not activate %s, error returned: %s", params.activateDefault.String(), errs.JoinMessage(err)))
}
case !params.isUpdate && terminal.IsTerminal(int(os.Stdin.Fd())) && os.Getenv(constants.InstallerNoSubshell) != "true" && os.Getenv("TERM") != "dumb":
Expand Down
3 changes: 2 additions & 1 deletion cmd/state-offline-installer/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"github.com/ActiveState/cli/internal/analytics"
"github.com/ActiveState/cli/internal/analytics/client/sync"
anaConst "github.com/ActiveState/cli/internal/analytics/constants"
"github.com/ActiveState/cli/internal/captain"
"github.com/ActiveState/cli/internal/config"
"github.com/ActiveState/cli/internal/constants"
Expand Down Expand Up @@ -78,7 +79,7 @@ func main() {
return
}

an = sync.New(cfg, nil, out)
an = sync.New(anaConst.SrcOfflineInstaller, cfg, nil, out)

prime := primer.New(
nil, out, nil,
Expand Down
3 changes: 2 additions & 1 deletion cmd/state-offline-uninstaller/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"github.com/ActiveState/cli/internal/analytics"
"github.com/ActiveState/cli/internal/analytics/client/sync"
anaConst "github.com/ActiveState/cli/internal/analytics/constants"
"github.com/ActiveState/cli/internal/captain"
"github.com/ActiveState/cli/internal/config"
"github.com/ActiveState/cli/internal/constants"
Expand Down Expand Up @@ -78,7 +79,7 @@ func main() {
return
}

an = sync.New(cfg, nil, out)
an = sync.New(anaConst.SrcOfflineInstaller, cfg, nil, out)

prime := primer.New(
nil, out, nil,
Expand Down
3 changes: 2 additions & 1 deletion cmd/state-remote-installer/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

"github.com/ActiveState/cli/internal/analytics"
"github.com/ActiveState/cli/internal/analytics/client/sync"
anaConst "github.com/ActiveState/cli/internal/analytics/constants"
"github.com/ActiveState/cli/internal/captain"
"github.com/ActiveState/cli/internal/config"
"github.com/ActiveState/cli/internal/constants"
Expand Down Expand Up @@ -94,7 +95,7 @@ func main() {
return
}

an = sync.New(cfg, nil, out)
an = sync.New(anaConst.SrcStateRemoteInstaller, cfg, nil, out)

// Set up prompter
prompter := prompt.New(true, an)
Expand Down
14 changes: 8 additions & 6 deletions cmd/state-svc/internal/resolver/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,9 @@ func New(cfg *config.Instance, an *sync.Client, auth *authentication.Auth) (*Res

usageChecker := rtusage.NewChecker(cfg, auth)

anForClient := sync.New(cfg, auth, nil)
// Note: source does not matter here, as analytics sent via the resolver have a source
// (e.g. State Tool or Executor), and that source will be used.
anForClient := sync.New(anaConsts.SrcStateTool, cfg, auth, nil)
return &Resolver{
cfg,
msg,
Expand Down Expand Up @@ -159,10 +161,10 @@ func (r *Resolver) Projects(ctx context.Context) ([]*graph.Project, error) {
return projects, nil
}

func (r *Resolver) AnalyticsEvent(_ context.Context, category, action string, _label *string, dimensionsJson string) (*graph.AnalyticsEventResponse, error) {
func (r *Resolver) AnalyticsEvent(_ context.Context, category, action, source string, _label *string, dimensionsJson string) (*graph.AnalyticsEventResponse, error) {
defer func() { handlePanics(recover(), debug.Stack()) }()

logging.Debug("Analytics event resolver: %s - %s", category, action)
logging.Debug("Analytics event resolver: %s - %s (%s)", category, action, source)

label := ""
if _label != nil {
Expand All @@ -188,12 +190,12 @@ func (r *Resolver) AnalyticsEvent(_ context.Context, category, action string, _l
return nil
})

r.anForClient.EventWithLabel(category, action, label, dims)
r.anForClient.EventWithSourceAndLabel(category, action, source, label, dims)

return &graph.AnalyticsEventResponse{Sent: true}, nil
}

func (r *Resolver) ReportRuntimeUsage(_ context.Context, pid int, exec string, dimensionsJSON string) (*graph.ReportRuntimeUsageResponse, error) {
func (r *Resolver) ReportRuntimeUsage(_ context.Context, pid int, exec, source string, dimensionsJSON string) (*graph.ReportRuntimeUsageResponse, error) {
defer func() { handlePanics(recover(), debug.Stack()) }()

logging.Debug("Runtime usage resolver: %d - %s", pid, exec)
Expand All @@ -202,7 +204,7 @@ func (r *Resolver) ReportRuntimeUsage(_ context.Context, pid int, exec string, d
return &graph.ReportRuntimeUsageResponse{Received: false}, errs.Wrap(err, "Could not unmarshal")
}

r.rtwatch.Watch(pid, exec, dims)
r.rtwatch.Watch(pid, exec, source, dims)

return &graph.ReportRuntimeUsageResponse{Received: true}, nil
}
Expand Down
7 changes: 4 additions & 3 deletions cmd/state-svc/internal/rtwatcher/entry.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ import (
)

type entry struct {
PID int `json:"pid"`
Exec string `json:"exec"`
Dims *dimensions.Values `json:"dims"`
PID int `json:"pid"`
Exec string `json:"exec"`
Source string `json:"source"`
Dims *dimensions.Values `json:"dims"`
}

func (e entry) IsRunning() (bool, error) {
Expand Down
8 changes: 4 additions & 4 deletions cmd/state-svc/internal/rtwatcher/watcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ type Watcher struct {
}

type analytics interface {
Event(category string, action string, dim ...*dimensions.Values)
EventWithSource(category, action, source string, dim ...*dimensions.Values)
}

func New(cfg *config.Instance, an analytics) *Watcher {
Expand Down Expand Up @@ -97,7 +97,7 @@ func (w *Watcher) check() {

func (w *Watcher) RecordUsage(e entry) {
logging.Debug("Recording usage of %s (%d)", e.Exec, e.PID)
w.an.Event(anaConst.CatRuntimeUsage, anaConst.ActRuntimeHeartbeat, e.Dims)
w.an.EventWithSource(anaConst.CatRuntimeUsage, anaConst.ActRuntimeHeartbeat, e.Source, e.Dims)
}

func (w *Watcher) Close() error {
Expand All @@ -116,10 +116,10 @@ func (w *Watcher) Close() error {
return nil
}

func (w *Watcher) Watch(pid int, exec string, dims *dimensions.Values) {
func (w *Watcher) Watch(pid int, exec, source string, dims *dimensions.Values) {
logging.Debug("Watching %s (%d)", exec, pid)
dims.Sequence = ptr.To(-1) // sequence is meaningless for heartbeat events
e := entry{pid, exec, dims}
e := entry{pid, exec, source, dims}
w.watching = append(w.watching, e)
go w.RecordUsage(e) // initial event
}
Loading

0 comments on commit 1335957

Please sign in to comment.