Documentation
¶
Overview ¶
Package telemetry — daemon log forwarder. The daemon's combined stdout/stderr lands in $XDG_STATE_HOME/clawtool/daemon.log. Every goroutine panic, every "clawtool: <subsystem>: <error>" stderr line, every BIAM reap warning ends up there — but it's local- only, so a daemon stuck in a panic loop on someone else's host is invisible to us until they file an issue.
LogWatcher tails the daemon log starting from EOF (so we never stream the historical buffer), classifies lines into severity + event_kind taxonomies, redacts known secret shapes, rate- limits to keep a panicking daemon from flooding PostHog, and emits `clawtool.daemon.log_event` events through the existing telemetry client. NO log-line bodies cross the wire — only the classification fields, so an env-value or path that happens to be in the log can't leak.
Wired in server.go after telemetry.New: one watcher per daemon boot, cancelled via context on shutdown.
Package telemetry — anonymous, opt-in PostHog event emission for clawtool (ADR-014 F5, gemini's R4 pick).
Strict guarantee: never emits prompts, paths, file contents, secrets, or env values. The CLI dispatcher strips arg slices before passing to Track; we additionally allow-list the keys that can ride on a payload.
Per ADR-007 we wrap github.com/posthog/posthog-go. The client is nil-safe; passing nil to Track is a no-op so call sites don't need to gate every call.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func EmitInstallOnce ¶ added in v0.22.4
EmitInstallOnce fires a `clawtool.install` event the first time it's called on a host AND the telemetry client is enabled. A marker file under $XDG_DATA_HOME/clawtool/install-emitted ensures every subsequent call is a no-op. Daemon boot is the natural place to call this — by the time `clawtool serve` runs on a fresh install we've already initialised the telemetry client and the marker can be created safely.
install_method comes from $CLAWTOOL_INSTALL_METHOD which the install.sh / brew formula / go install wrapper sets at install time. Empty / unrecognised falls through to "unknown" so we still get the event, just without source attribution.
The marker write happens BEFORE the Track call so a posthog outage can't cause repeated events on each retry. Worst case: we lose one install event entirely. Better than counting a single install ten times because the network was flaky.
func SetDebug ¶ added in v0.22.34
func SetDebug(on bool)
SetDebug toggles the debug trace at runtime. Wired from `clawtool serve --debug` so the operator can flip it without touching env.
func SilentDisabled ¶
func SilentDisabled() bool
SilentDisabled tells callers whether the env var explicitly disables telemetry regardless of config (for the "kill switch" use case operators want before talking on conference Wi-Fi).
Types ¶
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client wraps a PostHog client + the per-host anonymous distinct ID. Nil-safe: `(*Client)(nil).Track(...)` is a clean no-op.
sessionID groups every event emitted from a single daemon / CLI invocation under one $session_id property — PostHog's Sessions view + funnel queries rely on this to reconstruct "user did A then B then C in the same run" rather than treating every event as an isolated row. Generated fresh on New(), so a daemon restart starts a new session (which is the right boundary for CLI tools — different invocations are different units of work).
func New ¶
func New(cfg config.TelemetryConfig) *Client
New initialises the client when telemetry is enabled. Disabled config returns a nil-friendly client (Track is a no-op). Init failures degrade silently — telemetry is never load-bearing.
API key precedence: cfg.APIKey > cogitavePostHogKey baked-in default. Same for host. Operator-provided values always win so a self-hosted PostHog instance can capture the data instead of the shared cogitave project.
func (*Client) Enabled ¶
Enabled reports whether the client will actually emit. Useful for hot-path skips on expensive payload construction.
func (*Client) Track ¶
Track emits one event. Properties outside the allow-list are silently dropped. Safe to call on a nil receiver.
The c.client nil-check happens under c.mu so a Track racing a Close (which sets c.client = nil) can't dereference a nil posthog.Client. Pre-fix this checked nil OUTSIDE the lock then called Enqueue inside the lock — a Close that won the lock-race nil'd the field, and the next Track passed the outside-check only to nil-deref under the lock.
type LogWatcher ¶ added in v0.22.36
type LogWatcher struct {
// contains filtered or unexported fields
}
LogWatcher tails a log file and forwards classified events to a telemetry client. One watcher per daemon process. Run is the blocking entrypoint; cancel via the context.
func NewLogWatcher ¶ added in v0.22.36
func NewLogWatcher(tc *Client, path string) *LogWatcher
NewLogWatcher constructs a watcher. tc may be nil (no-op) or a disabled client (also no-op — the Track method short-circuits). path is the daemon log path (typically daemon.LogPath()).
func (*LogWatcher) Run ¶ added in v0.22.36
func (w *LogWatcher) Run(ctx context.Context)
Run blocks until ctx is cancelled. Tails path from EOF, classifies each new line, redacts content, emits classification-only events at most logEventPerMinuteCap per minute. Open errors are logged once via the debug seam and the watcher exits — there's no daemon log on a fresh host until the daemon writes its first line, but server.go arranges for that to happen before this is called.