fault turns a recovered panic into an error you can inspect with the standard
errors package, and attaches a call stack to it.
func TrySomething() (retErr error) {
defer fault.PanicToError(&retErr)
...
// if this code panics, TrySomething returns a PanicError
}
if panicErr, ok := errors.AsType[fault.PanicError](err); ok {
log.Print(panicErr.Value)
}
if withStack, ok := errors.AsType[fault.WithStack](err); ok {
log.Print(withStack.Stack())
}
PanicError unwraps as the value passed to panic when that value is an error,
so errors.Is and errors.As keep working through the panic boundary.
CaptureStack stores program counters and nothing else. Resolving them to
function names, files and line numbers happens in Frames and String, at
reporting time — which, for an error that is handled rather than logged, may
never happen at all.
WrapStack attaches a stack to an error unless it already carries one, so the
stack records where a failure originated rather than where it was last wrapped.
Stack entries are printed through ShortenFunction, which strips the path of
the running binary's main module, so example.com/app/service.Run prints as
service.Run. File names are left exactly as the runtime reports them —
absolute build-machine paths, or module-relative ones when the binary is built
with -trimpath.
Error types here are comparable, which is why Stack is held behind a pointer.
Copyright Onboard Inc.
Licensed under Apache 2.0 license.
Author: Alexey Feldgendler
With additional contributions from Mikhail Gusarov.