github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/internal/engine/telemetry/controller.go (about) 1 package telemetry 2 3 import ( 4 "context" 5 "fmt" 6 "io" 7 "os/exec" 8 "time" 9 10 "github.com/tilt-dev/tilt/internal/build" 11 "github.com/tilt-dev/tilt/internal/store" 12 "github.com/tilt-dev/tilt/internal/tracer" 13 "github.com/tilt-dev/tilt/pkg/logger" 14 "github.com/tilt-dev/tilt/pkg/model" 15 "github.com/tilt-dev/tilt/pkg/model/logstore" 16 ) 17 18 type Controller struct { 19 spans tracer.SpanSource 20 clock build.Clock 21 runCounter int 22 lastRunAt time.Time 23 } 24 25 func NewController(clock build.Clock, spans tracer.SpanSource) *Controller { 26 return &Controller{ 27 clock: clock, 28 spans: spans, 29 runCounter: 0, 30 } 31 } 32 33 func (t *Controller) OnChange(ctx context.Context, st store.RStore, _ store.ChangeSummary) error { 34 state := st.RLockState() 35 ts := state.TelemetrySettings 36 tc := ts.Cmd 37 st.RUnlockState() 38 39 period := ts.Period 40 if period == 0 { 41 period = model.DefaultTelemetryPeriod 42 } 43 44 if tc.Empty() || !t.lastRunAt.Add(period).Before(t.clock.Now()) { 45 return nil 46 } 47 48 t.runCounter++ 49 50 defer func() { 51 // wrap in a func so we get the time at the end of this function, not the beginning 52 t.lastRunAt = t.clock.Now() 53 }() 54 55 r, requeueFn, err := t.spans.GetOutgoingSpans() 56 if err != nil { 57 if err != io.EOF { 58 t.logError(st, fmt.Errorf("Error gathering Telemetry data for experimental_telemetry_cmd %v", err)) 59 } 60 return nil 61 } 62 63 // run the command with the contents of the spans as jsonlines on stdin 64 cmd := exec.CommandContext(ctx, tc.Argv[0], tc.Argv[1:]...) 65 cmd.Dir = ts.Workdir 66 cmd.Stdin = r 67 68 out, err := cmd.CombinedOutput() 69 if err != nil { 70 t.logError(st, fmt.Errorf("Telemetry command failed: %v\noutput: %s", err, out)) 71 requeueFn() 72 return nil 73 } 74 return nil 75 } 76 77 func (t *Controller) logError(st store.RStore, err error) { 78 spanID := logstore.SpanID(fmt.Sprintf("telemetry:%d", t.runCounter)) 79 st.Dispatch(store.NewLogAction(model.MainTiltfileManifestName, spanID, logger.InfoLvl, nil, []byte(err.Error()))) 80 }