@@ -8,14 +8,17 @@ import (
8
8
"io"
9
9
"os"
10
10
"runtime/trace"
11
+ "sort"
12
+ "strconv"
11
13
"strings"
12
14
"time"
13
15
14
- "github.com/getsentry/sentry-go"
15
16
segment "github.com/segmentio/analytics-go"
16
17
"github.com/spf13/cobra"
18
+ "github.com/spf13/pflag"
17
19
"go.jetpack.io/devbox"
18
- "go.jetpack.io/devbox/internal/boxcli/usererr"
20
+ "go.jetpack.io/devbox/internal/boxcli/featureflag"
21
+ "go.jetpack.io/devbox/internal/build"
19
22
"go.jetpack.io/devbox/internal/cloud/envir"
20
23
"go.jetpack.io/devbox/internal/telemetry"
21
24
)
@@ -27,60 +30,56 @@ import (
27
30
// 2. Data is only stored in SOC 2 compliant systems, and we are SOC 2 compliant ourselves.
28
31
// 3. Users should always have the ability to opt-out.
29
32
func Telemetry () Middleware {
30
-
31
- opts := telemetry .InitOpts ()
32
-
33
- return & telemetryMiddleware {
34
- opts : * opts ,
35
- disabled : telemetry .IsDisabled (opts ),
36
- }
33
+ return & telemetryMiddleware {}
37
34
}
38
35
39
36
type telemetryMiddleware struct {
40
- // Setup:
41
- opts telemetry.Opts
42
- disabled bool
43
-
44
37
// Used during execution:
45
38
startTime time.Time
46
-
47
- executionID string
48
39
}
49
40
50
41
// telemetryMiddleware implements interface Middleware (compile-time check)
51
42
var _ Middleware = (* telemetryMiddleware )(nil )
52
43
53
- func (m * telemetryMiddleware ) withExecutionID (execID string ) Middleware {
54
- m .executionID = execID
55
- return m
56
- }
57
-
58
44
func (m * telemetryMiddleware ) preRun (cmd * cobra.Command , args []string ) {
59
45
m .startTime = telemetry .CommandStartTime ()
60
46
47
+ telemetry .Start (telemetry .AppDevbox )
61
48
ctx := cmd .Context ()
62
49
defer trace .StartRegion (ctx , "telemetryPreRun" ).End ()
63
- if m . disabled {
50
+ if ! telemetry . Enabled () {
64
51
trace .Log (ctx , "telemetry" , "telemetry is disabled" )
65
52
return
66
53
}
67
- sentry := telemetry .NewSentry (m .opts .SentryDSN )
68
- sentry .Init (m .opts .AppName , m .opts .AppVersion , m .executionID )
69
54
}
70
55
71
56
func (m * telemetryMiddleware ) postRun (cmd * cobra.Command , args []string , runErr error ) {
72
57
defer trace .StartRegion (cmd .Context (), "telemetryPostRun" ).End ()
73
- if m .disabled {
58
+ defer telemetry .Stop ()
59
+
60
+ meta := telemetry.Metadata {
61
+ FeatureFlags : featureflag .All (),
62
+ CloudRegion : envir .GetRegion (),
63
+ CloudCache : os .Getenv ("DEVBOX_CACHE" ),
64
+ }
65
+
66
+ subcmd , flags , err := getSubcommand (cmd , args )
67
+ if err != nil {
68
+ // Ignore invalid commands/flags.
74
69
return
75
70
}
71
+ meta .Command = subcmd .CommandPath ()
72
+ meta .CommandFlags = flags
73
+ meta .Packages , meta .NixpkgsHash = getPackagesAndCommitHash (cmd )
74
+ meta .InShell , _ = strconv .ParseBool (os .Getenv ("DEVBOX_SHELL_ENABLED" ))
75
+ meta .InBrowser , _ = strconv .ParseBool (os .Getenv ("START_WEB_TERMINAL" ))
76
+ meta .InCloud = envir .IsDevboxCloud ()
77
+ telemetry .Error (runErr , meta )
76
78
77
79
evt := m .newEventIfValid (cmd , args , runErr )
78
80
if evt == nil {
79
81
return
80
82
}
81
-
82
- m .trackError (evt ) // Sentry
83
-
84
83
m .trackEvent (evt ) // Segment
85
84
}
86
85
@@ -105,7 +104,7 @@ type event struct {
105
104
// a valid event.
106
105
func (m * telemetryMiddleware ) newEventIfValid (cmd * cobra.Command , args []string , runErr error ) * event {
107
106
108
- subcmd , subargs , parseErr := getSubcommand (cmd , args )
107
+ subcmd , flags , parseErr := getSubcommand (cmd , args )
109
108
if parseErr != nil {
110
109
// Ignore invalid commands
111
110
return nil
@@ -126,16 +125,16 @@ func (m *telemetryMiddleware) newEventIfValid(cmd *cobra.Command, args []string,
126
125
127
126
return & event {
128
127
Event : telemetry.Event {
129
- AnonymousID : telemetry .DeviceID () ,
130
- AppName : m . opts . AppName ,
131
- AppVersion : m . opts . AppVersion ,
128
+ AnonymousID : telemetry .DeviceID ,
129
+ AppName : telemetry . AppDevbox ,
130
+ AppVersion : build . Version ,
132
131
CloudRegion : envir .GetRegion (),
133
132
Duration : time .Since (m .startTime ),
134
- OsName : telemetry .OS (),
133
+ OsName : build .OS (),
135
134
UserID : userID ,
136
135
},
137
136
Command : subcmd .CommandPath (),
138
- CommandArgs : subargs ,
137
+ CommandArgs : flags ,
139
138
CommandError : runErr ,
140
139
// The command is hidden if either the top-level command is hidden or
141
140
// the specific sub-command that was executed is hidden.
@@ -149,38 +148,15 @@ func (m *telemetryMiddleware) newEventIfValid(cmd *cobra.Command, args []string,
149
148
}
150
149
}
151
150
152
- func (m * telemetryMiddleware ) trackError (evt * event ) {
153
- // Ensure error is not nil and not a non-loggable user error
154
- if evt == nil || ! usererr .ShouldLogError (evt .CommandError ) {
155
- return
156
- }
157
-
158
- sentry .ConfigureScope (func (scope * sentry.Scope ) {
159
- scope .SetTag ("command" , evt .Command )
160
- scope .SetContext ("command" , map [string ]interface {}{
161
- "command" : evt .Command ,
162
- "command args" : evt .CommandArgs ,
163
- "packages" : evt .Packages ,
164
- "nixpkgs hash" : evt .CommitHash ,
165
- "in shell" : evt .InDevboxShell ,
166
- })
167
- scope .SetContext ("devbox env" , evt .DevboxEnv )
168
- })
169
- sentry .CaptureException (evt .CommandError )
170
- }
171
-
172
151
func (m * telemetryMiddleware ) trackEvent (evt * event ) {
173
152
if evt == nil || evt .CommandHidden {
174
153
return
175
154
}
176
155
177
156
if evt .CommandError != nil {
178
- // verified with manual testing that the sentryID returned by CaptureException
179
- // is the same as m.ExecutionID, since we set EventID = m.ExecutionID in sentry.Init
180
- evt .SentryEventID = m .executionID
157
+ evt .SentryEventID = telemetry .ExecutionID
181
158
}
182
-
183
- segmentClient := telemetry .NewSegmentClient (m .opts .TelemetryKey )
159
+ segmentClient := telemetry .NewSegmentClient (build .TelemetryKey )
184
160
defer func () {
185
161
_ = segmentClient .Close ()
186
162
}()
@@ -219,13 +195,18 @@ func (m *telemetryMiddleware) trackEvent(evt *event) {
219
195
})
220
196
}
221
197
222
- func getSubcommand (c * cobra.Command , args []string ) (subcmd * cobra.Command , subargs []string , err error ) {
223
- if c .TraverseChildren {
224
- subcmd , subargs , err = c .Traverse (args )
198
+ func getSubcommand (cmd * cobra.Command , args []string ) (subcmd * cobra.Command , flags []string , err error ) {
199
+ if cmd .TraverseChildren {
200
+ subcmd , _ , err = cmd .Traverse (args )
225
201
} else {
226
- subcmd , subargs , err = c .Find (args )
202
+ subcmd , _ , err = cmd .Find (args )
227
203
}
228
- return subcmd , subargs , err
204
+
205
+ subcmd .Flags ().Visit (func (f * pflag.Flag ) {
206
+ flags = append (flags , "--" + f .Name )
207
+ })
208
+ sort .Strings (flags )
209
+ return subcmd , flags , err
229
210
}
230
211
231
212
func getPackagesAndCommitHash (c * cobra.Command ) ([]string , string ) {
0 commit comments