Skip to content
Permalink
Browse files
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 <timofey.kirillov@flant.com>
  • Loading branch information
distorhead committed Jul 14, 2022
1 parent b92c9e9 commit 340242453ceb906d814dcfb31d76ab4b95318e1d
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 21 deletions.
@@ -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 {
@@ -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} }

This file was deleted.

@@ -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...)
@@ -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) {}
@@ -25,17 +25,28 @@ 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 {
handleErrorFunc func(err error)
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,17 +159,17 @@ 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)
}
span.SetAttributes(attribute.Key("eventData").String(string(rawEventData)))
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
}

0 comments on commit 3402424

Please sign in to comment.