Documentation
¶
Overview ¶
Package crashlog captures, stores, and formats crash reports for grut. Reports are scrubbed of personally identifiable information before being written to disk or formatted for GitHub issue submission.
Index ¶
- func CaptureCmdPanic(panicVal any, ctx string) string
- func Clear() (int, error)
- func CollectDiagnostics() map[string]string
- func DefaultLogTail() []string
- func FormatGitHubIssueBody(r *CrashReport) string
- func FormatGitHubIssueTitle(r *CrashReport) string
- func GuardTUI(ctx string)
- func LastCrashPath() string
- func RecoverAndReport(ctx string, tail *LogTailHandler)
- func ScrubPII(s string) string
- func SetDefaultLogTail(h *LogTailHandler)
- func Write(report *CrashReport) (string, error)
- func WriteRecovery(panicVal any, ctx string, tail *LogTailHandler)
- type CrashReport
- type LogTailHandler
- func (h *LogTailHandler) Enabled(ctx context.Context, level slog.Level) bool
- func (h *LogTailHandler) Entries() []string
- func (h *LogTailHandler) Handle(ctx context.Context, r slog.Record) error
- func (h *LogTailHandler) WithAttrs(attrs []slog.Attr) slog.Handler
- func (h *LogTailHandler) WithGroup(name string) slog.Handler
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func CaptureCmdPanic ¶ added in v0.7.1
CaptureCmdPanic writes a crash report for a panic recovered inside a Bubble Tea command goroutine and records it as the last crash path. Unlike WriteRecovery it prints nothing to stderr: the TUI still owns the terminal (alternate screen, raw mode), so stray output would corrupt the display. It returns the crash report path, or "" if the report could not be written.
Bubble Tea runs each command in its own goroutine and turns any panic there into a fatal ErrProgramPanic that tears the whole program down. GuardTUI only covers the model's Init/Update/View on the main goroutine, so a command that panics — e.g. on a nil field in live API data — would otherwise kill the TUI. Recovering in the command closure and reporting via this function keeps the TUI alive while preserving the stack for diagnosis. Use it from a deferred recover in the command:
return func() (msg tea.Msg) {
defer func() {
if r := recover(); r != nil {
crashlog.CaptureCmdPanic(r, "gitinfo.loadGitHubData")
msg = errorToast(r)
}
}()
...
}
func Clear ¶
Clear deletes all crash report files from the crash directory and returns the number of files removed.
func CollectDiagnostics ¶
CollectDiagnostics gathers safe, non-PII system information that is useful for triaging crash reports.
func DefaultLogTail ¶
func DefaultLogTail() []string
DefaultLogTail returns the entries from the package-level log tail handler. If no handler has been set, it returns nil.
func FormatGitHubIssueBody ¶
func FormatGitHubIssueBody(r *CrashReport) string
FormatGitHubIssueBody returns the full markdown body for a GitHub issue.
func FormatGitHubIssueTitle ¶
func FormatGitHubIssueTitle(r *CrashReport) string
FormatGitHubIssueTitle returns a short title for a GitHub issue.
func GuardTUI ¶ added in v0.7.0
func GuardTUI(ctx string)
GuardTUI is a deferred panic guard for the Bubble Tea model's Update, View, and Init methods. Bubble Tea catches panics inside those methods itself and returns only a generic ErrProgramPanic, so the top-level recover in main never sees the panic value and no crash report is written. GuardTUI closes that gap: it recovers the in-flight panic, writes a crash report with the preserved stack (debug.Stack in a deferred recover still points at the original panic site) and the recent log tail, records the report path, then re-panics with the original value so Bubble Tea still restores the terminal.
Use it as the first statement of the guarded method:
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
defer crashlog.GuardTUI("tui.Update")
...
}
func LastCrashPath ¶ added in v0.7.0
func LastCrashPath() string
LastCrashPath returns the filesystem path of the most recent crash report captured by GuardTUI during this run, or "" if no TUI panic was captured.
func RecoverAndReport ¶
func RecoverAndReport(ctx string, tail *LogTailHandler)
RecoverAndReport is intended to be called inside a deferred function at the top of main (or any critical goroutine). If a panic occurred it captures a crash report, writes it to disk, and prints guidance to stderr. If tail is non-nil its entries override the default log tail.
Usage:
defer func() { crashlog.RecoverAndReport("running root command", tail) }()
func ScrubPII ¶
ScrubPII removes personally identifiable information from s. It replaces home directory paths with ~, strips embedded credentials from HTTPS URLs, and redacts common token/key/secret patterns.
func SetDefaultLogTail ¶
func SetDefaultLogTail(h *LogTailHandler)
SetDefaultLogTail stores h as the package-level log tail handler so that DefaultLogTail can retrieve buffered entries without an explicit reference.
func Write ¶
func Write(report *CrashReport) (string, error)
Write serialises report as indented JSON and writes it to the crash directory. It returns the full path of the created file. Old reports beyond the retention limit are pruned automatically.
func WriteRecovery ¶
func WriteRecovery(panicVal any, ctx string, tail *LogTailHandler)
WriteRecovery creates and writes a crash report from an already-recovered panic value. Unlike RecoverAndReport, it does not call recover() itself, so the caller retains control of recovery flow (e.g., setting exit codes).
Types ¶
type CrashReport ¶
type CrashReport struct {
ID string `json:"id"`
Timestamp time.Time `json:"timestamp"`
Version string `json:"version"`
GoVersion string `json:"go_version"`
OS string `json:"os"`
Arch string `json:"arch"`
Terminal string `json:"terminal"`
PanicValue string `json:"panic_value"`
StackTrace string `json:"stack_trace"`
Context string `json:"context"`
LogTail []string `json:"log_tail"`
}
CrashReport holds all information captured when grut crashes.
func List ¶
func List() ([]*CrashReport, error)
List reads all crash reports from the crash directory and returns them sorted newest-first. Files that fail to parse are skipped with a warning logged.
func NewReport ¶
func NewReport(panicVal any, stack []byte, context string) *CrashReport
NewReport creates a CrashReport with auto-populated system information. The panicVal and stack come from the recover/debug.Stack calls, and context describes what grut was doing when the crash occurred.
func Read ¶
func Read(id string) (*CrashReport, error)
Read loads a single crash report by its ID. It returns an error if no report with the given ID exists.
type LogTailHandler ¶
type LogTailHandler struct {
// contains filtered or unexported fields
}
LogTailHandler is a slog.Handler that keeps a ring buffer of recent log entries while forwarding all records to an inner handler. The buffered entries can be retrieved after a crash to include in the report.
func NewLogTailHandler ¶
func NewLogTailHandler(inner slog.Handler, size int) *LogTailHandler
NewLogTailHandler creates a handler that buffers the most recent size log records in a ring buffer and delegates to inner for actual output.
func (*LogTailHandler) Enabled ¶
Enabled reports whether the inner handler is enabled for the given level.
func (*LogTailHandler) Entries ¶
func (h *LogTailHandler) Entries() []string
Entries returns the buffered log entries in chronological order (oldest first). It is safe to call from any goroutine.
func (*LogTailHandler) Handle ¶
Handle formats the record, stores it in the ring buffer, then delegates to the inner handler.