Skip to content

feat: persistent error logging for hook events#55

Merged
scotthavird merged 1 commit into
mainfrom
feat/error-logging
May 17, 2026
Merged

feat: persistent error logging for hook events#55
scotthavird merged 1 commit into
mainfrom
feat/error-logging

Conversation

@scotthavird
Copy link
Copy Markdown
Contributor

The bug

Hook send-event failures were silent. fileLog (cmd/hook.go) early-returned unless debug: true was set, the async subprocess set stderr = nil (internal/client/api.go), and even the existing debug-only log went to /tmp/promptconduit-hook.log — a path that's blown away on reboot and confusingly collides with the macOS app's own log file.

Net effect: when events stopped reaching the platform (network blip, expired key, server 5xx), the user had no on-disk trace to diagnose from.

The fix

New internal/logger package:

  • Error(format, args...)always written, regardless of config.
  • Debug(format, args...) — only written after SetDebug(true) (the hook entry points call this from the loaded config).
  • Lives at ~/.config/promptconduit/logs/promptconduit.log (XDG-aware).
  • Rotates at 1 MiB to a single .log.1 backup; total footprint ≤ 2 MiB.
  • Standalone — no dependency on client package, so it's safe to call from anywhere including the hot hook path.

Wiring:

  • cmd/hook.go: every failure path now calls logger.Error; trace lines use logger.Debug; legacy /tmp/promptconduit-hook.log writer removed.
  • internal/client/api.go: the async subprocess now inherits a fd pointing at the log file as Stderr, so a panic that escapes the in-process logger still leaves a trace.

Plus a new promptconduit logs command:

promptconduit logs              # last 50 lines
promptconduit logs -n 200       # last 200 lines
promptconduit logs --follow     # tail -f (uses /usr/bin/tail when available)
promptconduit logs --path       # just print the path
promptconduit logs --clear      # truncate

Verification

End-to-end on macOS:

Success path (debug=true config):

DEBUG pid=84525 Hook started
DEBUG pid=84525 Detected tool: claude-code, hook event: PreToolUse
DEBUG pid=84537 Async subprocess: envelope sent successfully

Failure path (bad API URL, debug=false in config):

ERROR pid=85171 Async subprocess API send failed (url=http://127.0.0.1:1): dial tcp 127.0.0.1:1: connect: connection refused

Exactly one ERROR line, zero DEBUG noise. Goal achieved: silent failures are now loud, verbose tracing stays off unless explicitly enabled.

Test plan

  • go test ./internal/logger/ — 5 tests pass (Error always writes, Debug gated, rotation at MaxBytes, Tail returns last N lines, Tail handles missing file)
  • make test — full suite green
  • go vet ./... clean
  • Real hook with successful API send → DEBUG breadcrumbs in log
  • Real hook with unreachable API + debug=false → exactly one ERROR line in log
  • promptconduit logs --path, --clear, -n, --follow all work
  • Windows: the --follow fallback to polling kicks in when tail is absent

🤖 Generated with Claude Code

Hook send-event failures (network errors, missing config, malformed
payloads) were previously swallowed: `fileLog` early-returned unless
`debug: true` was set, and the async subprocess discarded stderr. When
events stopped reaching the platform, there was no on-disk trace to
diagnose from.

This change introduces `internal/logger` — a small package with:

  Error  always written, regardless of config
  Debug  written only when SetDebug(true) has been called

Logs live at ~/.config/promptconduit/logs/promptconduit.log (XDG-aware)
and rotate at 1 MiB; only one backup is kept (~2 MiB cap per machine).

Wiring:
  - cmd/hook.go: every failure path now calls logger.Error; trace lines
    use logger.Debug; the legacy /tmp/promptconduit-hook.log helper is
    removed.
  - internal/client/api.go: the async subprocess inherits an fd pointing
    at the log file as stderr, so a panic/crash that escapes the logger
    still leaves a trace.

Plus a new `promptconduit logs` command with -n/--tail, --follow,
--path, and --clear flags.

Verified end-to-end on macOS:
  - Successful send: DEBUG breadcrumbs only (debug=true config).
  - Forced failure (bad API URL, debug=false): one ERROR line in the
    log; no DEBUG noise.
@scotthavird scotthavird merged commit d3c7b38 into main May 17, 2026
1 check passed
@scotthavird scotthavird deleted the feat/error-logging branch May 17, 2026 05:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant