Documentation
¶
Overview ¶
Package terr implements a set of functions for tracing errors in Go 1.20+.
Example ¶
This example shows how to combine different terr functions and print an error tracing tree at the end.
package main import ( "fmt" "github.com/alnvdl/terr" ) func main() { err := terr.Newf("base") traced := terr.Trace(err) wrapped := terr.Newf("wrapped: %w", traced) masked := terr.Newf("masked: %v", wrapped) fmt.Printf("%@\n", masked) }
Output:
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Newf ¶
Newf works exactly like fmt.Errorf, but returns a traced error. All traced errors passed as formatting arguments are included as children, regardless of the formatting verbs used for these errors. This function is equivalent to fmt.Errorf("...", ...). If used without verbs and additional arguments, it is equivalent to errors.New("...").
Example ¶
This example shows how Newf interacts with traced and non-traced errors. Traced errors are included in the trace regardless of the fmt verb used for them, while non-traced errors are handled as fmt.Errorf would, but they do not get included in the trace.
package main import ( "errors" "fmt" "github.com/alnvdl/terr" ) func main() { nonTracedErr := errors.New("non-traced") tracedErr1 := terr.Newf("traced 1") tracedErr2 := terr.Newf("traced 2") newErr := terr.Newf("errors: %w, %v, %w", nonTracedErr, tracedErr1, tracedErr2) fmt.Printf("%@\n", newErr) fmt.Println("---") // errors.Is works. fmt.Println("newErr is nonTracedErr:", errors.Is(newErr, nonTracedErr)) fmt.Println("newErr is tracedErr1:", errors.Is(newErr, tracedErr1)) fmt.Println("newErr is tracedErr2:", errors.Is(newErr, tracedErr2)) }
Output:
func Trace ¶
Trace returns a new traced error for err. If err is already a traced error, a new traced error will be returned containing err as a child traced error. No wrapping or masking takes place in this function.
Example ¶
This example shows how Trace interacts with traced and non-traced errors.
package main import ( "errors" "fmt" "github.com/alnvdl/terr" ) func main() { // Adds tracing information to non-traced errors. nonTracedErr := errors.New("non-traced") fmt.Printf("%@\n", terr.Trace(nonTracedErr)) fmt.Println("---") // Adds another level of tracing information to traced errors. tracedErr := terr.Newf("traced") fmt.Printf("%@\n", terr.Trace(tracedErr)) }
Output:
func TraceSkip ¶ added in v1.0.14
TraceSkip works exactly like Trace, but lets the caller skip a number of stack frames when detecting the error location, with 0 identifying the caller of TraceSkip. This function can be used in custom error constructor functions, so they can return a traced error pointing at their callers.
Example ¶
This example shows how to use TraceSkip to add tracing when using two common patterns in error constructors: wrapped sentinel errors and custom error types. TraceSkip works by accepting a number of stack frames to skip when defining the location of the traced errors it returns. In this example, the location is being set to the location of the callers of the error constructors, and not the constructors themselves.
package main import ( "errors" "fmt" "github.com/alnvdl/terr" ) var ErrConnection = errors.New("connection error") func connectionError(text string) error { return terr.TraceSkip(fmt.Errorf("%w: %s", ErrConnection, text), 1) } type ValidationError struct{ field, msg string } func (e *ValidationError) Error() string { return e.msg } func NewValidationError(field, msg string) error { return terr.TraceSkip(&ValidationError{field, msg}, 1) } func main() { // It is considered a good practice in Go to never return sentinel errors // directly, but rather to wrap them like we do with connectionError here, // so they can be turned into custom errors later if needed, without // breaking callers in the process. // err1 will be annotated with the line number of the following line. err1 := connectionError("timeout") fmt.Printf("%@\n", err1) // errors.Is works. fmt.Println("\tIs ErrConnection:", errors.Is(err1, ErrConnection)) fmt.Println("---") // If an error needs to include more details, a custom error type needs to // be used. // err2 will be annotated with the line number of the following line. err2 := NewValidationError("x", "x must be >= 0") fmt.Printf("%@\n", err2) // errors.As works. var customErr *ValidationError ok := errors.As(err2, &customErr) fmt.Println("\tIs ValidationError:", ok) fmt.Println("\tCustom error field:", customErr.field) fmt.Println("\tCustom error message:", customErr.msg) }
Output:
Types ¶
type ErrorTracer ¶ added in v1.0.12
type ErrorTracer interface { // error is the underlying error. error // Location identifies the file and line where error was created, traced, // wrapped or masked. Location() (string, int) // Children returns other traced errors that were traced, wrapped or // masked by this traced error. Children() []ErrorTracer }
ErrorTracer is an object capable of tracing an error's location and possibly other related errors, forming an error tracing tree. Please note that implementing ErrorTracer is not enough to make an error a traced error: only errors returned by functions in this package are considered traced errors.
func TraceTree ¶
func TraceTree(err error) ErrorTracer
TraceTree returns the root of the n-ary error tracing tree for err. Returns nil if err is not a traced error. This function can be used to represent the error tracing tree using custom formats.
Example ¶
This example shows how to use the n-ary error tracing tree returned by TraceTree.
package main import ( "errors" "fmt" "github.com/alnvdl/terr" ) func main() { nonTracedErr := errors.New("non-traced") tracedErr1 := terr.Newf("traced 1") tracedErr2 := terr.Newf("traced 2") newErr := terr.Newf("%w, %v, %w", nonTracedErr, tracedErr1, tracedErr2) printNode := func(node terr.ErrorTracer) { fmt.Printf("Error: %v\n", node.Error()) file, line := node.Location() fmt.Printf("Location: %s:%d\n", file, line) fmt.Printf("Children: %v\n", node.Children()) fmt.Println("---") } node := terr.TraceTree(newErr) printNode(node) printNode(node.Children()[0]) printNode(node.Children()[1]) }
Output: