Documentation
¶
Overview ¶
Package errs provides Go 1.13+ compatible error wrapping with call stacks and function parameters.
This package extends the standard library's error handling with:
- Automatic call stack capture for error context
- Function parameter tracking for detailed debugging
- Helper functions for common error patterns (NotFound, context errors)
- Panic recovery and conversion to errors
- Iterator support for Go 1.23+
Basic usage:
func DoSomething(id string) (err error) {
defer errs.WrapWithFuncParams(&err, id)
// Your code here
return someOperation(id)
}
See the documentation of individual functions for more examples.
Index ¶
- Variables
- func As[T error](err error) []T
- func AsError(val any) error
- func AsErrorWithDebugStack(val any) error
- func DontLog(err error) error
- func Errorf(format string, a ...any) error
- func Has[T error](err error) bool
- func IsContextCanceled(ctx context.Context) bool
- func IsContextDeadlineExceeded(ctx context.Context) bool
- func IsContextDone(ctx context.Context) bool
- func IsContextError(err error) bool
- func IsErrNotFound(err error) bool
- func IsOtherThanErrNotFound(err error) bool
- func IsType(err, ref error) bool
- func IterSeq(err error) iter.Seq[error]
- func IterSeq2[T any](err error) iter.Seq2[T, error]
- func LogFunctionCall(logger Logger, function string, params ...any)
- func LogPanicWithFuncParams(log Logger, params ...any)
- func New(text string) error
- func RecoverAndLogPanicWithFuncParams(log Logger, params ...any)
- func RecoverPanicAsError(result *error)
- func RecoverPanicAsErrorWithFuncParams(result *error, params ...any)
- func ReplaceErrNotFound(err, replacement error) error
- func Root(err error) error
- func ShouldLog(err error) bool
- func Type[T error](err error) bool
- func UnwrapCallStack(err error) error
- func WrapWith0FuncParams(resultVar *error)
- func WrapWith10FuncParams(resultVar *error, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9 any)
- func WrapWith1FuncParam(resultVar *error, p0 any)
- func WrapWith2FuncParams(resultVar *error, p0, p1 any)
- func WrapWith3FuncParams(resultVar *error, p0, p1, p2 any)
- func WrapWith4FuncParams(resultVar *error, p0, p1, p2, p3 any)
- func WrapWith5FuncParams(resultVar *error, p0, p1, p2, p3, p4 any)
- func WrapWith6FuncParams(resultVar *error, p0, p1, p2, p3, p4, p5 any)
- func WrapWith7FuncParams(resultVar *error, p0, p1, p2, p3, p4, p5, p6 any)
- func WrapWith8FuncParams(resultVar *error, p0, p1, p2, p3, p4, p5, p6, p7 any)
- func WrapWith9FuncParams(resultVar *error, p0, p1, p2, p3, p4, p5, p6, p7, p8 any)
- func WrapWithCallStack(err error) error
- func WrapWithCallStackSkip(skip int, err error) error
- func WrapWithFuncParams(resultVar *error, params ...any)
- func WrapWithFuncParamsSkip(skip int, resultVar *error, params ...any)
- type LogDecisionMaker
- type Logger
- type Secret
- type Sentinel
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( // TrimFilePathPrefix will be trimmed from the // beginning of every call-stack file-path. // Defaults to $GOPATH/src/ of the build environment // or will be empty if go build gets called with -trimpath. TrimFilePathPrefix = filePathPrefix() // MaxCallStackFrames is the maximum number of frames to include in the call stack. MaxCallStackFrames = 32 // Printer is the pretty.Printer used to format function parameters // in error call stacks. It can be configured to customize formatting, // mask secrets, or adapt types that don't implement pretty.Printable. // // Example - Masking sensitive data: // // func init() { // errs.Printer.AsPrintable = func(v reflect.Value) (pretty.Printable, bool) { // if v.Kind() == reflect.String && strings.Contains(v.String(), "secret") { // return printableAdapter{ // format: func(w io.Writer) { // fmt.Fprint(w, "`***REDACTED***`") // }, // }, true // } // return pretty.AsPrintable(v) // Use default // } // } Printer = &pretty.DefaultPrinter // FormatParamMaxLen is the maximum length in bytes for a single formatted // parameter value in error call stacks. When a parameter's formatted // representation exceeds this length, it will be truncated and suffixed // with "…(TRUNCATED)". // // This prevents extremely large values (like long strings, big JSON blobs, // or large data structures) from making error messages unreadable. // // Default: 5000 bytes // // Example: // // errs.FormatParamMaxLen = 100 // Limit to 100 bytes // // func ProcessData(data string) (err error) { // defer errs.WrapWithFuncParams(&err, data) // // If data is 200 bytes, error will show: // // ProcessData("first 100 bytes of data…(TRUNCATED)") // return validateData(data) // } FormatParamMaxLen = 5000 )
Configuration variables
var FormatFunctionCall = func(function string, params ...any) string { var b strings.Builder b.WriteString(function) b.WriteByte('(') for i, param := range params { if i > 0 { b.WriteString(", ") } var paramBuf bytes.Buffer Printer.Fprint(¶mBuf, param) if paramBuf.Len() > FormatParamMaxLen { bStr := paramBuf.Bytes()[:FormatParamMaxLen] b.Write(bytes.ToValidUTF8(bStr, nil)) b.WriteString("…(TRUNCATED)") } else { b.Write(paramBuf.Bytes()) } } b.WriteByte(')') return b.String() }
FormatFunctionCall formats a function call in pseudo syntax using the Printer variable to format parameters. Types that implement pretty.Printable will use their PrettyPrint method, and this works recursively for nested structs.
FormatFunctionCall is a function variable that can be changed to globally configure the formatting of function calls.
Default Implementation:
The default implementation formats function calls as:
functionName(param1, param2, ...)
Each parameter is formatted using the Printer variable, which respects types implementing pretty.Printable. If a formatted parameter exceeds FormatParamMaxLen bytes, it will be truncated to ensure valid UTF-8 and suffixed with "…(TRUNCATED)".
Functions ¶
func As ¶
As returns all errors of type T in the wrapping tree of err.
This function is similar to errors.As but traverses the full tree using the interface methods:
Unwrap() error Unwrap() []error
An error err matches the type T if the type assertion err.(T) holds, or if the error has a method As(any) bool such that err.As(target) returns true when target is a non-nil *T. In the latter case, the As method is responsible for setting target.
func AsError ¶
AsError converts any type to an error without wrapping it. Nil values will be converted to a nil error.
func AsErrorWithDebugStack ¶
AsErrorWithDebugStack converts any type to an error and if not nil wraps it with debug.Stack() after a newline. Nil values will be converted to a nil error.
func DontLog ¶
DontLog wraps the passed error as LogDecisionMaker so that ShouldLog returns false. A nil error won't be wrapped but returned as nil.
func Errorf ¶
Errorf wraps the result of fmt.Errorf with the current call stack.
If the format specifier includes a %w verb with an error operand, the returned error will implement an Unwrap method returning the operand. It is invalid to include more than one %w verb or to supply it with an operand that does not implement the error interface. The %w verb is otherwise a synonym for %v.
func IsContextCanceled ¶
IsContextCanceled checks if the context Done channel is closed and if the context error unwraps to context.Canceled.
func IsContextDeadlineExceeded ¶
IsContextDeadlineExceeded checks if the context Done channel is closed and if the context error unwraps to context.DeadlineExceeded.
func IsContextDone ¶
IsContextDone checks if the context Done channel is closed.
func IsContextError ¶
IsContextError returns true if err unwraps to context.Canceled or context.DeadlineExceeded.
func IsErrNotFound ¶
IsErrNotFound returns true if the passed error unwraps to, or is ErrNotFound, sql.ErrNoRows, or os.ErrNotExist.
func IsOtherThanErrNotFound ¶
IsOtherThanErrNotFound returns true if the passed error is not nil and does not unwrap to, or is ErrNotFound, sql.ErrNoRows, or os.ErrNotExist.
func IsType ¶
IsType returns if err or any unwrapped error is of the type of the passed ref error. It works similar than errors.As but without assigning to the ref error and without checking for Is or As methods.
func IterSeq ¶
IterSeq returns an iter.Seq[error] iterator that yields the passed error once.
See the iter package documentation for more details.
func IterSeq2 ¶
IterSeq2 returns an iter.Seq2[T, error] iterator that yields the default value of T and the passed error once.
This is useful for the design-pattern of using first value of a two value iterator as actual value and the second as error.
See the iter package documentation for more details.
func LogFunctionCall ¶
LogFunctionCall logs a formatted function call using FormatFunctionCall if logger is not nil. This is useful for logging function calls with their parameters for debugging.
func LogPanicWithFuncParams ¶
LogPanicWithFuncParams recovers any panic, converts it to an error wrapped with the callstack of the panic and the passed function parameter values and prints it with the prefix "LogPanicWithFuncParams: " to the passed Logger. After logging, the original panic is re-paniced.
func RecoverAndLogPanicWithFuncParams ¶
RecoverAndLogPanicWithFuncParams recovers any panic, converts it to an error wrapped with the callstack of the panic and the passed function parameter values and prints it with the prefix "RecoverAndLogPanicWithFuncParams: " to the passed Logger.
func RecoverPanicAsError ¶
func RecoverPanicAsError(result *error)
RecoverPanicAsError recovers any panic, converts it to an error wrapped with the callstack of the panic and assigns it to the result error.
func RecoverPanicAsErrorWithFuncParams ¶
RecoverPanicAsErrorWithFuncParams recovers any panic, converts it to an error wrapped with the callstack of the panic and the passed function parameter values and assigns it to the result error.
func ReplaceErrNotFound ¶
ReplaceErrNotFound returns the passed replacement error if IsErrNotFound(err) returns true, meaning that all (optionally wrapped) ErrNotFound, sql.ErrNoRows, os.ErrNotExist errors get replaced.
func ShouldLog ¶
ShouldLog checks if the passed error unwraps as a LogDecisionMaker and returns the result of its ShouldLog method. If error does not unwrap to LogDecisionMaker and is not nil then ShouldLog returns true. A nil error results in false.
func Type ¶
Type indicates if err is not nil and it or any unwrapped error is of the type T. It works similar than errors.As but without assigning to the ref error and without checking for Is or As methods.
func UnwrapCallStack ¶
UnwrapCallStack removes all top-level callstack wrapping from err and returns the underlying error without the callstack information.
Unlike Root which unwraps to the root cause, this function only removes callstack wrappers (including those with function parameters) but preserves the error chain.
This is useful when you want to compare or match errors without the callstack information affecting the comparison, or when you need to access the wrapped error while discarding debug information.
Examples:
// Remove callstack wrapper from error
err := errs.New("something failed")
cleaned := errs.UnwrapCallStack(err)
// cleaned is the underlying Sentinel without callstack
// Compare errors without callstack
err1 := errs.WrapWithCallStack(sentinel)
err2 := errs.WrapWithCallStack(sentinel)
// err1 != err2 (different callstacks)
// errs.UnwrapCallStack(err1) == errs.UnwrapCallStack(err2) == sentinel
// Preserve error chain while removing top-level callstack
wrapped := fmt.Errorf("context: %w", sentinel)
withStack := errs.WrapWithCallStack(wrapped)
result := errs.UnwrapCallStack(withStack)
// result == wrapped (still wraps sentinel)
Note: This only removes top-level callstack wrapping. If there are callstack wrappers further down the error chain, they are preserved.
func WrapWith0FuncParams ¶
func WrapWith0FuncParams(resultVar *error)
WrapWith0FuncParams wraps an error with the call stack for functions with no parameters. This is more efficient than WrapWithFuncParams for zero-parameter functions.
func WrapWith10FuncParams ¶
WrapWith10FuncParams wraps an error with the call stack and 10 function parameters. This is more efficient than WrapWithFuncParams for ten-parameter functions.
func WrapWith1FuncParam ¶
WrapWith1FuncParam wraps an error with the call stack and 1 function parameter. This is more efficient than WrapWithFuncParams for single-parameter functions.
func WrapWith2FuncParams ¶
WrapWith2FuncParams wraps an error with the call stack and 2 function parameters. This is more efficient than WrapWithFuncParams for two-parameter functions.
func WrapWith3FuncParams ¶
WrapWith3FuncParams wraps an error with the call stack and 3 function parameters. This is more efficient than WrapWithFuncParams for three-parameter functions.
func WrapWith4FuncParams ¶
WrapWith4FuncParams wraps an error with the call stack and 4 function parameters. This is more efficient than WrapWithFuncParams for four-parameter functions.
func WrapWith5FuncParams ¶
WrapWith5FuncParams wraps an error with the call stack and 5 function parameters. This is more efficient than WrapWithFuncParams for five-parameter functions.
func WrapWith6FuncParams ¶
WrapWith6FuncParams wraps an error with the call stack and 6 function parameters. This is more efficient than WrapWithFuncParams for six-parameter functions.
func WrapWith7FuncParams ¶
WrapWith7FuncParams wraps an error with the call stack and 7 function parameters. This is more efficient than WrapWithFuncParams for seven-parameter functions.
func WrapWith8FuncParams ¶
WrapWith8FuncParams wraps an error with the call stack and 8 function parameters. This is more efficient than WrapWithFuncParams for eight-parameter functions.
func WrapWith9FuncParams ¶
WrapWith9FuncParams wraps an error with the call stack and 9 function parameters. This is more efficient than WrapWithFuncParams for nine-parameter functions.
func WrapWithCallStack ¶
WrapWithCallStack wraps an error with the current call stack.
func WrapWithCallStackSkip ¶
WrapWithCallStackSkip wraps an error with the current call stack skipping skip stack frames.
The skip parameter specifies how many stack frames to skip before capturing the call stack. Use skip=0 to capture the stack from the immediate caller of WrapWithCallStackSkip. Increase skip by 1 for each additional function wrapper you add.
Examples:
// Direct use - skip=1 to show caller of your function
func DoSomething() error {
err := someOperation()
if err != nil {
return WrapWithCallStackSkip(1, err)
}
return nil
}
// Wrapper function - skip=1+skip to pass through skip count
func myWrapper(skip int, err error) error {
return WrapWithCallStackSkip(1+skip, err)
}
// Helper that wraps - skip=1 so caller of helper appears in stack
func wrapDatabaseError(err error) error {
return WrapWithCallStackSkip(1, fmt.Errorf("database error: %w", err))
}
func WrapWithFuncParams ¶
WrapWithFuncParams wraps an error with the current call stack and function parameters.
This is the most commonly used function for wrapping errors with function parameters in defer statements. It automatically captures the correct call stack frame.
Example:
func ProcessUser(userID string, age int) (err error) {
defer WrapWithFuncParams(&err, userID, age)
if age < 0 {
return errors.New("invalid age")
}
// When an error is returned, it will be wrapped with:
// - The call stack showing ProcessUser(userID, age)
// - The actual parameter values passed to the function
return someOperation(userID)
}
Note: For functions with 0-10 parameters, use the optimized variants WrapWith0FuncParams through WrapWith10FuncParams for better performance.
Example ¶
package main
import (
"context"
"fmt"
)
type strct struct {
A int
}
func funcA(ctx context.Context, i int, s string, strct *strct) (err error) {
defer WrapWith4FuncParams(&err, ctx, i, s, strct)
return funcB(s, "X\nX")
}
func funcB(s ...string) (err error) {
defer WrapWithFuncParams(&err, s)
return funcC()
}
func funcC() (err error) {
defer WrapWithFuncParams(&err)
return New("error in funcC")
}
func main() {
err := funcA(context.Background(), 666, "Hello World!", &strct{A: -1})
fmt.Println(err)
}
Output: error in funcC github.com/domonda/go-errs.funcC() github.com/domonda/go-errs/wrapwithfuncparams_test.go:27 github.com/domonda/go-errs.funcB([`Hello World!`,`X\nX`]) github.com/domonda/go-errs/wrapwithfuncparams_test.go:21 github.com/domonda/go-errs.funcA(Context{}, 666, `Hello World!`, strct{A:-1}) github.com/domonda/go-errs/wrapwithfuncparams_test.go:15
func WrapWithFuncParamsSkip ¶
WrapWithFuncParamsSkip wraps an error with the current call stack and function parameters, skipping skip stack frames.
The skip parameter specifies how many stack frames to skip before capturing the call stack. Use skip=0 to capture the stack from the immediate caller of WrapWithFuncParamsSkip. Increase skip by 1 for each additional function wrapper you add.
This function is typically used in defer statements to automatically wrap errors with the function's parameters when the function returns an error.
Examples:
// Standard usage in defer - skip=0 is correct
func ProcessUser(userID string, age int) (err error) {
defer WrapWithFuncParamsSkip(0, &err, userID, age)
// ... function body
return someError
}
// Wrapper function - skip=1+skip to pass through skip count
func myWrapperSkip(skip int, resultVar *error, params ...any) {
WrapWithFuncParamsSkip(1+skip, resultVar, params...)
}
// Most common: use WrapWithFuncParams which has skip=0 built-in
func ProcessUser(userID string, age int) (err error) {
defer WrapWithFuncParams(&err, userID, age)
// ... function body
return someError
}
Types ¶
type LogDecisionMaker ¶
type LogDecisionMaker interface {
error
// ShouldLog decides if the error should be logged
ShouldLog() bool
}
LogDecisionMaker can be implemented by errors to decide if they should be logged. Use the package function ShouldLog to check if a wrapped error implements the interface and get the result of its ShouldLog method.
type Secret ¶
type Secret interface {
// Secrect returns the wrapped secret value.
Secrect() any
// String returns a redacted string that indicates
// that the value is a secret without revealing the actual value.
String() string
}
Secret is an interface that wraps a secret value to prevent it from being logged or printed. It implements CallStackPrintable to ensure secrets are never revealed in error call stacks.
func KeepSecret ¶
KeepSecret wraps the passed value in a Secret to prevent it from being logged or printed.
Example ¶
secret := errs.KeepSecret("My Password!")
// Actual value:
fmt.Println(secret.Secrect())
// Redacted string variants:
fmt.Println(secret)
fmt.Printf("%v\n", secret)
fmt.Printf("%+v\n", secret)
fmt.Printf("%#v\n", secret)
Output: My Password! ***REDACTED*** ***REDACTED*** ***REDACTED*** string(***REDACTED***)
type Sentinel ¶
type Sentinel string
Sentinel implements the error interface for a string and is meant to be used to declare const sentinel errors.
Example:
const ErrUserNotFound errs.Sentinel = "user not found"
Example ¶
const ErrUserAlreadyExists Sentinel = "user already exists"
var err error = ErrUserAlreadyExists
fmt.Println("const Sentinel errors.Is:", errors.Is(err, ErrUserAlreadyExists))
err = fmt.Errorf("%w: [email protected]", ErrUserAlreadyExists)
fmt.Println("Wrapped Sentinel errors.Is:", errors.Is(err, ErrUserAlreadyExists))
Output: const Sentinel errors.Is: true Wrapped Sentinel errors.Is: true
const ErrNotFound Sentinel = "not found"
ErrNotFound is an universal error returned in case that a requested resource could not be found.
Recommended usage:
This error can be returned directly from a function if that function only requests one kind of resource and no further differentiation is needed about what resource could not be found.
Else create custom "not found" error by wrapping ErrNotFound or implementing a custom error type with an
Is(target error) bool
method that returns true for target == ErrNotFound.
For checking errors it is recommended to use IsErrNotFound(err) instead of errors.Is(err, ErrNotFound) to also catch the standard library "not found" errors sql.ErrNoRows and os.ErrNotExist.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
go-errs-wrap
command
go-errs-wrap is a code transformation tool for managing error wrapping in Go.
|
go-errs-wrap is a code transformation tool for managing error wrapping in Go. |
|
go-errs-wrap/rewrite
Package rewrite provides functionality to remove or replace defer errs.Wrap statements and //#wrap-result-err marker comments in Go source files.
|
Package rewrite provides functionality to remove or replace defer errs.Wrap statements and //#wrap-result-err marker comments in Go source files. |
|
examples
|
|
|
wrapping
command
|