From 340242453ceb906d814dcfb31d76ab4b95318e1d Mon Sep 17 00:00:00 2001 From: Timofey Kirillov Date: Thu, 14 Jul 2022 18:46:28 +0300 Subject: [PATCH] feat(telemetry): anonimized cli options usage, exit-code event, tune timeouts * Send CommandStarted event with additional data: list of cli options names that has been specified (without values). * Send CommandExited event with exit code. * Lower telemetry conn timeouts to 800ms. Signed-off-by: Timofey Kirillov --- cmd/werf/common/telemetry.go | 24 +++++++++++++++++- pkg/telemetry/event.go | 38 +++++++++++++++++++++++++++++ pkg/telemetry/event_type.go | 7 ------ pkg/telemetry/helpers.go | 2 +- pkg/telemetry/no_telemetrywerfio.go | 8 +++--- pkg/telemetry/telemetrywerfio.go | 37 +++++++++++++++++++++------- 6 files changed, 95 insertions(+), 21 deletions(-) create mode 100644 pkg/telemetry/event.go delete mode 100644 pkg/telemetry/event_type.go diff --git a/cmd/werf/common/telemetry.go b/cmd/werf/common/telemetry.go index 63a20cfb1f..b7cd04079f 100644 --- a/cmd/werf/common/telemetry.go +++ b/cmd/werf/common/telemetry.go @@ -7,6 +7,7 @@ import ( "github.com/go-git/go-git/v5/plumbing/transport" "github.com/spf13/cobra" + flag "github.com/spf13/pflag" "github.com/werf/werf/pkg/git_repo" "github.com/werf/werf/pkg/telemetry" @@ -34,6 +35,8 @@ func InitTelemetry(ctx context.Context) { } func ShutdownTelemetry(ctx context.Context, exitCode int) { + telemetry.GetTelemetryWerfIO().CommandExited(ctx, exitCode) + if err := telemetry.Shutdown(ctx); err != nil { telemetry.LogF("unable to shutdown: %s", err) } @@ -51,9 +54,28 @@ func TelemetryPreRun(cmd *cobra.Command, args []string) error { } InitTelemetry(ctx) - telemetry.GetTelemetryWerfIO().SetCommand(ctx, command) + var commandOptions []telemetry.CommandOption + for _, fs := range []*flag.FlagSet{cmd.Flags(), cmd.PersistentFlags(), cmd.LocalFlags(), cmd.InheritedFlags()} { + fs.VisitAll(func(f *flag.Flag) { + if !f.Changed { + return + } + + for _, opt := range commandOptions { + if opt.Name == f.Name { + return + } + } + + commandOptions = append(commandOptions, telemetry.CommandOption{ + Name: f.Name, + }) + }) + } + telemetry.GetTelemetryWerfIO().SetCommandOptions(ctx, commandOptions) + if projectID, err := getTelemetryProjectID(ctx); err != nil { telemetry.LogF("error: %s", err) } else { diff --git a/pkg/telemetry/event.go b/pkg/telemetry/event.go new file mode 100644 index 0000000000..766e7c908f --- /dev/null +++ b/pkg/telemetry/event.go @@ -0,0 +1,38 @@ +package telemetry + +type EventType string + +const ( + CommandStartedEvent EventType = "CommandStarted" + CommandExitedEvent EventType = "CommandExited" +) + +type Event interface { + GetType() EventType + GetData() interface{} +} + +func NewCommandStarted(commandOptions []CommandOption) *CommandStarted { + return &CommandStarted{commandOptions: commandOptions} +} + +type CommandStarted struct { + commandOptions []CommandOption +} + +func (e *CommandStarted) GetType() EventType { return CommandStartedEvent } +func (e *CommandStarted) GetData() interface{} { + if len(e.commandOptions) > 0 { + return map[string]interface{}{"commandOptions": e.commandOptions} + } + return nil +} + +func NewCommandExited(exitCode int) *CommandExited { return &CommandExited{exitCode: exitCode} } + +type CommandExited struct { + exitCode int +} + +func (e *CommandExited) GetType() EventType { return CommandExitedEvent } +func (e *CommandExited) GetData() interface{} { return map[string]interface{}{"exitCode": e.exitCode} } diff --git a/pkg/telemetry/event_type.go b/pkg/telemetry/event_type.go deleted file mode 100644 index aace76ec25..0000000000 --- a/pkg/telemetry/event_type.go +++ /dev/null @@ -1,7 +0,0 @@ -package telemetry - -type EventType string - -const ( - CommandStartedEvent EventType = "CommandStarted" -) diff --git a/pkg/telemetry/helpers.go b/pkg/telemetry/helpers.go index 11bb7b797a..6badb78212 100644 --- a/pkg/telemetry/helpers.go +++ b/pkg/telemetry/helpers.go @@ -25,7 +25,7 @@ func NewTraceExporter(url string) (*otlptrace.Exporter, error) { otlptracehttp.WithEndpoint(urlObj.Host), otlptracehttp.WithURLPath(urlObj.Path), otlptracehttp.WithRetry(otlptracehttp.RetryConfig{Enabled: false}), - otlptracehttp.WithTimeout(5*time.Second), + otlptracehttp.WithTimeout(800*time.Millisecond), ) client := otlptracehttp.NewClient(opts...) diff --git a/pkg/telemetry/no_telemetrywerfio.go b/pkg/telemetry/no_telemetrywerfio.go index 5e93b2476b..e7670f123a 100644 --- a/pkg/telemetry/no_telemetrywerfio.go +++ b/pkg/telemetry/no_telemetrywerfio.go @@ -4,6 +4,8 @@ import "context" type NoTelemetryWerfIO struct{} -func (t *NoTelemetryWerfIO) CommandStarted(context.Context) {} -func (t *NoTelemetryWerfIO) SetProjectID(context.Context, string) {} -func (t *NoTelemetryWerfIO) SetCommand(context.Context, string) {} +func (t *NoTelemetryWerfIO) CommandStarted(context.Context) {} +func (t *NoTelemetryWerfIO) SetProjectID(context.Context, string) {} +func (t *NoTelemetryWerfIO) SetCommand(context.Context, string) {} +func (t *NoTelemetryWerfIO) CommandExited(context.Context, int) {} +func (t *NoTelemetryWerfIO) SetCommandOptions(context.Context, []CommandOption) {} diff --git a/pkg/telemetry/telemetrywerfio.go b/pkg/telemetry/telemetrywerfio.go index b9b51fbe87..d59310b58d 100644 --- a/pkg/telemetry/telemetrywerfio.go +++ b/pkg/telemetry/telemetrywerfio.go @@ -25,7 +25,17 @@ const ( type TelemetryWerfIOInterface interface { SetProjectID(ctx context.Context, projectID string) SetCommand(ctx context.Context, command string) + SetCommandOptions(ctx context.Context, options []CommandOption) + CommandStarted(ctx context.Context) + CommandExited(ctx context.Context, exitCode int) +} + +type CommandOption struct { + Name string `json:"name"` + AsCli bool `json:"asCli"` + AsEnv bool `json:"asEnv"` + Count int `json:"count"` } type TelemetryWerfIO struct { @@ -33,9 +43,10 @@ type TelemetryWerfIO struct { tracerProvider *sdktrace.TracerProvider traceExporter *otlptrace.Exporter - executionID string - projectID string - command string + executionID string + projectID string + command string + commandOptions []CommandOption } type TelemetryWerfIOOptions struct { @@ -53,7 +64,7 @@ func NewTelemetryWerfIO(url string, opts TelemetryWerfIOOptions) (*TelemetryWerf tracerProvider: sdktrace.NewTracerProvider( sdktrace.WithBatcher(e, sdktrace.WithBatchTimeout(1*time.Millisecond), // send all available events immediately - sdktrace.WithExportTimeout(1000*time.Millisecond), + sdktrace.WithExportTimeout(800*time.Millisecond), ), ), traceExporter: e, @@ -100,12 +111,20 @@ func (t *TelemetryWerfIO) SetCommand(ctx context.Context, command string) { t.command = command } +func (t *TelemetryWerfIO) SetCommandOptions(ctx context.Context, options []CommandOption) { + t.commandOptions = options +} + func (t *TelemetryWerfIO) SetProjectID(ctx context.Context, projectID string) { t.projectID = projectID } func (t *TelemetryWerfIO) CommandStarted(ctx context.Context) { - t.sendEvent(ctx, CommandStartedEvent, nil) + t.sendEvent(ctx, NewCommandStarted(t.commandOptions)) +} + +func (t *TelemetryWerfIO) CommandExited(ctx context.Context, exitCode int) { + t.sendEvent(ctx, NewCommandExited(exitCode)) } func (t *TelemetryWerfIO) getAttributes() map[string]interface{} { @@ -121,7 +140,7 @@ func (t *TelemetryWerfIO) getAttributes() map[string]interface{} { return attributes } -func (t *TelemetryWerfIO) sendEvent(ctx context.Context, eventType EventType, eventData interface{}) error { +func (t *TelemetryWerfIO) sendEvent(ctx context.Context, event Event) error { trc := t.getTracer() _, span := trc.Start(ctx, spanName) @@ -140,9 +159,9 @@ func (t *TelemetryWerfIO) sendEvent(ctx context.Context, eventType EventType, ev } span.SetAttributes(attribute.Key("attributes").String(string(rawAttributes))) - span.SetAttributes(attribute.Key("eventType").String(string(eventType))) + span.SetAttributes(attribute.Key("eventType").String(string(event.GetType()))) - rawEventData, err := json.Marshal(eventData) + rawEventData, err := json.Marshal(event.GetData()) if err != nil { return fmt.Errorf("unable to marshal event data: %w", err) } @@ -150,7 +169,7 @@ func (t *TelemetryWerfIO) sendEvent(ctx context.Context, eventType EventType, ev span.SetAttributes(attribute.Key("schemaVersion").Int64(schemaVersion)) span.End() - LogF("sent event: ts=%d executionID=%q projectID=%q command=%q attributes=%q eventType=%q eventData=%q schemaVersion=%d", ts, t.executionID, t.projectID, t.command, string(rawAttributes), string(eventType), string(rawEventData), schemaVersion) + LogF("sent event: ts=%d executionID=%q projectID=%q command=%q attributes=%q eventType=%q eventData=%q schemaVersion=%d", ts, t.executionID, t.projectID, t.command, string(rawAttributes), string(event.GetType()), string(rawEventData), schemaVersion) return nil }