Documentation
¶
Overview ¶
Package telemetry defines event shapes and helpers for CLI usage telemetry.
Two event types are emitted per command invocation:
- CommandIntent: fired before execution captures command shape and runtime environment — never user-supplied values.
- CommandError: fired only on failure, after CommandIntent, captures an error category enum and elapsed time.
Neither event records flag values, positional args, env var values, or any other user-supplied data. [FlagsSet] contains flag names only, and cobra's Visit function means only explicitly-set declared flags appear.
Usage:
telemetry.SetBackend(myBackend) // once at startup
env := telemetry.NewEnv() // once at startup
telemetry.RecordIntent(CommandIntent{...})
if err != nil {
telemetry.RecordError(CommandError{...})
}
Index ¶
- Constants
- func DetectAgent() string
- func Disabled() bool
- func Init() (flush func())
- func RecordError(e CommandError)
- func RecordInstall(e InstallEvent)
- func RecordIntent(e CommandIntent)
- func ResolveInstallType() string
- func SetBackend(b Backend)
- func SetDisabled(v bool)
- func UserDomainFromEmail(email string) string
- type Backend
- type CommandError
- type CommandIntent
- type Env
- type ErrorCategory
- type InstallEvent
- type SegmentBackend
Constants ¶
const ( AgentClaudeCode = "claude-code" AgentCursor = "cursor" AgentGeminiCLI = "gemini-cli" AgentCodexCLI = "codex-cli" AgentAugment = "augment" AgentCline = "cline" AgentOpenCode = "opencode" AgentTrae = "trae" AgentDevin = "devin" )
AI agent identifiers. These are the values sent in the "aiagent" telemetry property — lowercase, hyphenated, stable across agent versions.
const ( InstallTypeScript = "script" InstallTypeBrew = "brew" InstallTypeUnknown = "unknown" )
InstallType values. Add new install methods here as they're wired up, and have the installer set hbase.EnvInstallType accordingly.
Variables ¶
This section is empty.
Functions ¶
func DetectAgent ¶
func DetectAgent() string
DetectAgent returns a standardized, lowercase identifier for the coding agent the CLI appears to be running under, or "" if none is detected. Detection relies on env vars each agent sets in its own subprocesses.
func Disabled ¶
func Disabled() bool
Disabled reports whether the user has opted out of telemetry, via either disable_telemetry in config.yaml or HARNESS_NO_TELEMETRY=1. Distinct from having no active backend (e.g. a dev build with no write key) — that case still logs debug output, whereas an explicit opt-out logs nothing.
func Init ¶
func Init() (flush func())
Init sets up the telemetry backend from config and returns a flush function to defer in main. Safe to call unconditionally — no-ops when telemetry is disabled or no write key is present.
func RecordError ¶
func RecordError(e CommandError)
RecordError emits a CommandError. Same gating as RecordIntent.
func RecordInstall ¶
func RecordInstall(e InstallEvent)
RecordInstall emits an InstallEvent. Same gating as RecordIntent.
func RecordIntent ¶
func RecordIntent(e CommandIntent)
RecordIntent emits a CommandIntent. No-op when the user has opted out (disable_telemetry or HARNESS_NO_TELEMETRY=1) — in that case it returns before even logging the debug line. When opted in but no backend is set (dev build, no write key), it still logs for debugging but sends nothing.
func ResolveInstallType ¶
func ResolveInstallType() string
ResolveInstallType reads hbase.EnvInstallType, defaulting to InstallTypeScript when unset and falling back to InstallTypeUnknown for any value outside the whitelist.
func SetBackend ¶
func SetBackend(b Backend)
SetBackend registers the active sink. Pass nil to disable. Call before any command executes.
func SetDisabled ¶
func SetDisabled(v bool)
SetDisabled sets the disabled flag from config.yaml's disable_telemetry field. Call once at startup after loading config.
func UserDomainFromEmail ¶
UserDomainFromEmail extracts the domain portion of an email address (e.g. "harness.io"). Returns empty string if email is empty or malformed.
Types ¶
type Backend ¶
type Backend interface {
RecordIntent(e CommandIntent)
RecordError(e CommandError)
RecordInstall(e InstallEvent)
}
Backend is implemented by telemetry sinks (Segment, debug-stdout, etc.).
type CommandError ¶
type CommandError struct {
// Mirror of CommandIntent identity fields for correlation.
Verb string
Noun string
Module string
AccountID string
UserDomain string
UserID string
UserType string
TokenKind string
AuthSource string
RunID string
Category ErrorCategory
DurationMs int64
Env Env
}
CommandError is emitted when a command exits with an error, paired with a prior CommandIntent for the same invocation.
type CommandIntent ¶
type CommandIntent struct {
// Verb/Noun/Module describe the command shape, e.g. "execute"/"pipeline"/"pipeline".
Verb string
Noun string
Module string
// FlagsSet holds the names of flags the user explicitly passed.
// Collected via cobra's cmd.Flags().Visit — only declared flags, never values.
FlagsSet []string
// AccountID from resolved auth. Empty for commands that skip auth.
AccountID string
// UserDomain is the domain portion of the user's profile email (e.g. "harness.io").
// Never the full email address.
UserDomain string
// UserID is the Harness user uuid from the resolved profile, or "" if unset.
UserID string
// UserType is config.UserTypeUser or config.UserTypeServiceAccount, or "" if unset.
UserType string
// TokenKind is the type of credential in use: "pat", "sat", "jwt", or "".
TokenKind string
// AuthSource is "profile" when auth came from config file, "env" when from env vars.
AuthSource string
// RunID correlates all API calls from this invocation. Mirrors hbase.RunID.
RunID string
Env Env
}
CommandIntent is emitted once per invocation before the command executes. It records who is running what and which flags were explicitly set.
type Env ¶
type Env struct {
OS string // runtime.GOOS
Arch string // runtime.GOARCH
Version string // hbase.Version
IsDev bool
IsTTY bool // stdout is an interactive terminal
IsPipelineExecution bool
PipelineID string // HARNESS_PIPELINEID; empty when IsPipelineExecution is false
// AIAgent is a standardized identifier for the coding agent the CLI is
// running under (e.g. "claude-code", "cursor"), or "" if none is detected.
// See [DetectAgent].
AIAgent string
Locale string // from LANG/LC_ALL/LC_CTYPE, e.g. "en_US.UTF-8"
TermProgram string // TERM_PROGRAM env var, e.g. "vscode", "iTerm.app"
}
Env captures static facts about the runtime environment. Call NewEnv once at startup and reuse the result across all events.
type ErrorCategory ¶
type ErrorCategory string
ErrorCategory is a coarse, enum-safe classification of a command failure. It must never contain user-supplied text.
const ( ErrorCategoryAuth ErrorCategory = "auth_error" ErrorCategoryAPI ErrorCategory = "api_error" ErrorCategoryNotFound ErrorCategory = "not_found" ErrorCategoryValidation ErrorCategory = "validation_error" ErrorCategoryInvalidVerb ErrorCategory = "invalid_verb" ErrorCategoryInvalidNoun ErrorCategory = "invalid_noun" ErrorCategoryInvalidFlag ErrorCategory = "invalid_flag" ErrorCategoryBadUsage ErrorCategory = "bad_usage" // fallback when more specific bucket can't be determined ErrorCategoryTimeout ErrorCategory = "timeout" ErrorCategoryUnknown ErrorCategory = "unknown" )
func ClassifyError ¶
func ClassifyError(err error) ErrorCategory
ClassifyError maps err to an ErrorCategory without inspecting any user-supplied message text. It relies on typed sentinel errors.
Currently handles:
To classify API errors (401/403/404), the client package needs to expose a typed error carrying the HTTP status code. Until then those fall through to ErrorCategoryUnknown.
type InstallEvent ¶
type InstallEvent struct {
RunID string
// InstallType identifies how the CLI was installed. See
// [ResolveInstallType] for the whitelist and default.
InstallType string
Env Env
}
InstallEvent is emitted once by an installer, via the hidden --post-install flag, right after a fresh binary is placed on disk.
type SegmentBackend ¶
type SegmentBackend struct {
// contains filtered or unexported fields
}
SegmentBackend implements Backend by posting directly to Segment's HTTP tracking API, sending each event on its own goroutine so callers never block on network I/O. Create with [NewSegmentBackend] and call SegmentBackend.Close (or defer it) before the process exits to give any in-flight requests a brief chance to land — see [closeGracePeriod].
func (*SegmentBackend) Close ¶
func (s *SegmentBackend) Close()
Close waits up to [closeGracePeriod] for any in-flight events to finish sending, then returns unconditionally. Call via defer in main before process exit.
func (*SegmentBackend) RecordError ¶
func (s *SegmentBackend) RecordError(e CommandError)
RecordError sends a "Command Error" track event.
func (*SegmentBackend) RecordInstall ¶
func (s *SegmentBackend) RecordInstall(e InstallEvent)
RecordInstall sends a "CLI Installed" track event.
func (*SegmentBackend) RecordIntent ¶
func (s *SegmentBackend) RecordIntent(e CommandIntent)
RecordIntent sends a "Command Run" track event.