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  }