telemetry

package
v1.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 26, 2026 License: Apache-2.0 Imports: 20 Imported by: 0

Documentation

Overview

Package telemetry emits one best-effort event per kongctl command execution. The package is backend-agnostic: it owns a stable internal schema (Event), centralizes error categorization (Categorize), and routes events through a replaceable Sink.

Telemetry is opt-out: enabled by default. Users opt out per-invocation with --no-telemetry, per-process with KONGCTL_NO_TELEMETRY=true or DO_NOT_TRACK=1, or persistently through the local telemetry preference file or telemetry.enabled=false in their profile config.

Index

Constants

View Source
const (
	ConfigKeyEnabled = "telemetry.enabled"
	// ConfigKeyDebug enables the local JSONL file sink. Events are only
	// persisted to disk when both telemetry.enabled and telemetry.debug
	// are true. Intended for kongctl developers verifying emitted events;
	// end users should leave this off so no telemetry data lands on their
	// machine.
	ConfigKeyDebug = "telemetry.debug"
)

Config keys read by NewRecorder. Defaults registered in internal/config/config.go.

View Source
const (
	// EnvNoTelemetry is the profile-agnostic kongctl kill switch, named to
	// mirror the --no-telemetry CLI flag. It is one-way: only "true"
	// (case-insensitive) disables telemetry. Any other value — including
	// "false", unset, or garbage — falls through to config.
	EnvNoTelemetry = "KONGCTL_NO_TELEMETRY"
	// EnvDoNotTrack is the cross-vendor hard kill switch. Highest priority.
	// Only the spec value "1" disables telemetry
	EnvDoNotTrack = "DO_NOT_TRACK"
)

Environment variables that override profile config for telemetry. Checked at the top of NewRecorder so users can toggle without needing to know which profile is active.

View Source
const PreferenceFileName = ".telemetry-enabled"

PreferenceFileName is the profile-agnostic, per-device telemetry opt-in/out file stored beside kongctl's config.yaml.

View Source
const SchemaVersion = 1

SchemaVersion identifies the shape of Event.

Variables

This section is empty.

Functions

func ContextWithRecorder

func ContextWithRecorder(ctx context.Context, rec *Recorder) context.Context

ContextWithRecorder returns ctx with rec attached. PersistentPreRun looks it up via FromContext to populate per-command fields.

func PreferenceFileExists

func PreferenceFileExists(cfg config.Hook) bool

func PreferenceFilePath

func PreferenceFilePath(cfg config.Hook) (string, error)

func ReadPreference

func ReadPreference(cfg config.Hook, logger *slog.Logger) (bool, bool)

func WritePreference

func WritePreference(cfg config.Hook, enabled bool) error

Types

type CommandInfo

type CommandInfo struct {
	Path string
}

CommandInfo is the data SetCommand expects from the root PersistentPreRun hook.

type Event

type Event struct {
	SchemaVersion int       `json:"schema_version"`
	Timestamp     time.Time `json:"timestamp"`

	Version string `json:"version"`
	OS      string `json:"os"`
	Arch    string `json:"arch"`

	CommandPath string `json:"command_path"`
}

Event is the single telemetry record kongctl emits per command execution.

type NoopSink

type NoopSink struct{}

NoopSink discards events. Used when telemetry is disabled so the hot path stays allocation-free.

func (NoopSink) Close

func (NoopSink) Close(_ context.Context) error

func (NoopSink) Emit

func (NoopSink) Emit(_ context.Context, _ Event) error

type Recorder

type Recorder struct {
	// contains filtered or unexported fields
}

Recorder buffers a single event for the duration of one command execution and flushes it to a Sink on Close. A Recorder is single-use: Begin → (one SetCommand) → Finalize → Close. When telemetry is disabled, NewRecorder returns a Recorder backed by NoopSink so the call shape stays uniform.

func FromContext

func FromContext(ctx context.Context) *Recorder

FromContext returns the Recorder attached to ctx, or nil if telemetry is disabled.

func NewRecorder

func NewRecorder(
	_ context.Context,
	cfg config.Hook,
	bi *build.Info,
	_ *iostreams.IOStreams,
	logger *slog.Logger,
	forceDisabled bool,
) *Recorder

NewRecorder builds a Recorder. It reads telemetry.enabled from cfg; if false (or forceDisabled is true), it returns a Recorder whose dispatch path is a no-op. forceDisabled is the per-invocation kill switch carried by the --no-telemetry CLI flag.

func (*Recorder) Close

func (r *Recorder) Close(_ context.Context) error

Close drains the dispatcher with a bounded deadline so a slow sink can never wedge command shutdown. Always returns nil — telemetry is best-effort.

func (*Recorder) Disable

func (r *Recorder) Disable(ctx context.Context) error

func (*Recorder) Enabled

func (r *Recorder) Enabled() bool

func (*Recorder) Finalize

func (r *Recorder) Finalize(end time.Time)

Finalize builds the final Event and enqueues it for dispatch. Non-blocking: if the channel is full, the event is dropped rather than risk blocking command shutdown.

func (*Recorder) SetCommand

func (r *Recorder) SetCommand(info CommandInfo)

SetCommand attaches the active leaf command's metadata. Called from the root PersistentPreRun once Cobra has resolved the leaf. The binary name is stripped from info.Path so emitted events carry e.g. "get apis" rather than "kongctl get apis" — every event is a kongctl invocation by definition, so the prefix is redundant on the wire.

A bare "kongctl" invocation (no subcommand) trims to "" and is treated as no-command: cmdSet stays false and Finalize will skip the event.

type Sink

type Sink interface {
	Emit(ctx context.Context, e Event) error
	Close(ctx context.Context) error
}

Sink is the replaceable transport for telemetry events. Implementations must be safe to call from a single dispatcher goroutine; concurrent Emit calls are not required.

Implementations MUST honor ctx cancellation and deadlines. The dispatcher passes a bounded context (see flushTimeout) into every Emit and Close call so that a network-backed sink cannot wedge the dispatcher goroutine past process exit. Long-running I/O (HTTP POSTs, file syncs) must abort when ctx is done.

func NewFileSink

func NewFileSink(path string) Sink

NewFileSink returns a sink that appends JSONL events to path. Each Emit opens, writes, and closes the file. kongctl emits one event per process, so caching the FD would add lifecycle complexity for no measurable win; revisit if event volume grows.

func NewMultiSink

func NewMultiSink(sinks ...Sink) Sink

NewMultiSink composes sinks. Nil children are filtered.

func NewUDPSink

func NewUDPSink(addr string) Sink

NewUDPSink returns a Sink that serializes events as Splunk key=value lines and writes one datagram per Emit. addr is host:port. The UDP socket is dialed lazily on first Emit so a configured-but-never-used sink imposes no startup cost.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL