diff --git a/cmd/fuzz_panic_test.go b/cmd/fuzz_panic_test.go index 5ac344d034..970ca17f0a 100644 --- a/cmd/fuzz_panic_test.go +++ b/cmd/fuzz_panic_test.go @@ -198,6 +198,7 @@ func isAutoGenerated(leaf leafCommand) bool { // "bundle", "auth", "sync", "fs", etc. The heuristic: anything whose // root isn't in this block list is auto-generated. manualRoots := map[string]bool{ + "aitools": true, "bundle": true, "auth": true, "sync": true, diff --git a/libs/telemetry/context.go b/libs/telemetry/context.go index e556e462cd..f0eb6ad60e 100644 --- a/libs/telemetry/context.go +++ b/libs/telemetry/context.go @@ -23,3 +23,11 @@ func fromContext(ctx context.Context) *logger { return v.(*logger) } + +// loggerFromContext returns the telemetry logger, or false if none was +// installed. Unlike fromContext it does not panic, so callers on paths that may +// run without telemetry setup can drop events instead of crashing. +func loggerFromContext(ctx context.Context) (*logger, bool) { + v, ok := ctx.Value(telemetryLoggerKey).(*logger) + return v, ok +} diff --git a/libs/telemetry/logger.go b/libs/telemetry/logger.go index cddc407c62..c92b7470f3 100644 --- a/libs/telemetry/logger.go +++ b/libs/telemetry/logger.go @@ -29,7 +29,16 @@ const ( ) func Log(ctx context.Context, event protos.DatabricksCliLog) { - fromContext(ctx).log(event) + // A missing logger means telemetry was never initialized on this context + // (e.g. a command invoked outside the normal cmd/root setup). Dropping the + // event is the right call: telemetry is best-effort and must never crash a + // command. + l, ok := loggerFromContext(ctx) + if !ok { + log.Debugf(ctx, "telemetry logger not found in the context; dropping event") + return + } + l.log(event) } type logger struct { diff --git a/libs/telemetry/logger_test.go b/libs/telemetry/logger_test.go index 59633403b3..e4ff717e6d 100644 --- a/libs/telemetry/logger_test.go +++ b/libs/telemetry/logger_test.go @@ -152,3 +152,14 @@ func TestTelemetryUploadMaxRetries(t *testing.T) { assert.EqualError(t, err, "failed to upload telemetry logs after three attempts") assert.Equal(t, 3, count) } + +func TestLogWithoutLoggerDropsEvent(t *testing.T) { + // A context without a telemetry logger must not panic; the event is dropped. + assert.NotPanics(t, func() { + Log(t.Context(), protos.DatabricksCliLog{ + CliTestEvent: &protos.CliTestEvent{ + Name: protos.DummyCliEnumValue1, + }, + }) + }) +}