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
- func ContextWithRecorder(ctx context.Context, rec *Recorder) context.Context
- func PreferenceFileExists(cfg config.Hook) bool
- func PreferenceFilePath(cfg config.Hook) (string, error)
- func ReadPreference(cfg config.Hook, logger *slog.Logger) (bool, bool)
- func WritePreference(cfg config.Hook, enabled bool) error
- type CommandInfo
- type Event
- type NoopSink
- type Recorder
- type Sink
Constants ¶
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.
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.
const PreferenceFileName = ".telemetry-enabled"
PreferenceFileName is the profile-agnostic, per-device telemetry opt-in/out file stored beside kongctl's config.yaml.
const SchemaVersion = 1
SchemaVersion identifies the shape of Event.
Variables ¶
This section is empty.
Functions ¶
func ContextWithRecorder ¶
ContextWithRecorder returns ctx with rec attached. PersistentPreRun looks it up via FromContext to populate per-command fields.
func PreferenceFileExists ¶
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.
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 ¶
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 ¶
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) Finalize ¶
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 ¶
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 ¶
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 ¶
NewMultiSink composes sinks. Nil children are filtered.
func NewUDPSink ¶
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.