zerrors

package module
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 18, 2020 License: Apache-2.0 Imports: 9 Imported by: 0

README

zerrors

GoDoc Report card

Package zerrors provides additional functionality on top of the go 1.13 error wrapping features, particularly frame information and flexibility over the wrapped error formatting, without sacrificing on performance.

Contents

  • package github.com/JavierZunzunegui/zerrors: the primary package, it is through this than one can wrap, inspect and serialise errors. Most users should only require this.
  • package github.com/JavierZunzunegui/zerrors/zmain: an auxiliary package to configure default behaviour, namely the format of Error(), format's %+v, as well as disabling frame capturing for improved performance. Most users will never require this, and for those who do use it it should be only used within the package initialisation phase (init() or in a global var).

How to use it

Replace any existing

if err != nil {
    return err
}

Or

if err != nil {
    return fmt.Errorf("additional info: %w", err)
}

Or any similar pattern, with:

if err != nil {
    return zerrors.SWrap(err, "additional info")
}

Or

if err != nil {
    return zerrors.Wrap(err, &CustomErrorType{})
}

Global errors should also be replaced to use zerrors.New or zerrors.SNew.

The resulting error's Error() string method is of format last message: ...: first wrap message: base message. A more detailed message is produced by zerrors.Detail(error) string or via the %+v pattern of the fmt methods, of the form last message (file.go:LINE): ... : first wrap message (file.go:LINE): base message (file.go:LINE).

Alternative serialisation of the error is also possible, either via a custom method (say, MyFormat(error) string) or by changing the default encodings via zmain. This way one can produce any alternative message (such as last message - ... - first wrap message - base message, or any variant with frame information), using the same inputs as the default encoding uses.

The errors.Is, errors.As and errors.Unwrap methods from go1.13 are supported as expected, and are intended to remain the primary means to examine the contents of errors. Two new additional methods, zerrors.Value(error) error and zerros.Frame(error) (runtime.Frame, bool) are introduced and can be used in conjunction with errors.Unwrap to implement more sophisticated functionality, such as the alternative serialisation formats. Error inspection via type assertion (discouraged since go1.13) is further discouraged, as it will be of no use for errors returned by zerrors.

More details and examples can be found in the various examples, tests and benchmarks in the source code.

General guidance

Package zerrors works best if no other errors implement interface{ Unwrap() error }, as these will not support frames, error formatting or the performance optimisations inside zerrors. Note libraries may use zerrors without those importing it knowing about it or changing their handling of errors, the callers will simply not make use of any of the additional functionality this package offers. Library callers - the package main - are best suited to make use of these, and through zerrors/zmain and custom formatters decide how all errors are serialised, including errors coming from external libraries.

Benchmarks

See benchmark/README.md. There are performance comparisons to all current mayor strategies for error wrapping: the standard library's errors.New and fmt.Errorf with the %w pattern, github.com/pkg/errors and golang.org/x/xerrors. Benchmarks show zerrors to have a better generally performance than all the above, while being more flexible.

Support and Future work

This is not an officially supported Google product.

As of Feb 2020, zerrors is new and remains untested in production. Its implementation is minimal and consists of only a few exported methods. Please contribute to improve this library by sharing your experience via an issue or pull request.

Documentation

Overview

Package zerrors provides error wrapping functionality.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Detail added in v0.2.0

func Detail(err error) string

Detail is an alternative error encoding to err.Error(), equivalent to the %+v format form. It can be called on any error but for non-zerror errors is encodes it in the standard %+v fmt form. For zerror errors, it uses the 'detail' function to serialise and it and is expected to (and by default, is) more detailed and expensive than err.Error() and contains frame information. By default this is in the form: "{err1} ({file}:{line}): {err2} ({file}:{line}): ... : {errN} ({file}:{line})".

func Frame

func Frame(err error) (runtime.Frame, bool)

Frame returns the frame contained at this particular node in the error chain. For non-wrapError errors or wrapError errors without a frame, it returns an empty frame and false. Otherwise it returns the populated frame and true.

func New

func New(err error) error

New produces a wrapError from a non-wrapError. If frame capture is enabled, it will get one at the caller of New. If err is a wrapError it returns itself, with no frame changes. This should be avoided.

func SNew

func SNew(s string) error

SNew produces a wrapError with a simple string error. If frame capture is enabled, it will get one at the caller of SNew.

func SWrap

func SWrap(inErr error, s string) error

SWrap produces a wrapError using a simple string error. The first argument is the error being wrapped, the second is the string error being added to it. If the first error is nil, Wrap does nothing and returns nil. Otherwise it returns a wrapError. If frame capture is enabled, it will get one at the caller of SWrap.

Example
package main

import (
	"errors"
	"fmt"
	"path"
	"strings"

	"github.com/JavierZunzunegui/zerrors"
)

func main() {
	err := zerrors.SNew("base")
	err = zerrors.SWrap(err, "first wrapper")
	fmt.Println(err)                          // basic standard format
	fmt.Println(zerrors.Detail(err))          // detail standard format
	fmt.Println(basicCustomErrorFormat(err))  // basic custom format
	fmt.Println(detailCustomErrorFormat(err)) // detail custom format
}

// An example custom alternative to err.Error().
func basicCustomErrorFormat(err error) string {
	var ss []string
	for ; err != nil; err = errors.Unwrap(err) {
		ss = append(ss, zerrors.Value(err).Error())
	}
	return strings.Join(ss, " - ")
}

// An example custom alternative to zerrors.Detail(err).
func detailCustomErrorFormat(err error) string {
	var ss []string
	for ; err != nil; err = errors.Unwrap(err) {
		s := zerrors.Value(err).Error()

		if frame, ok := zerrors.Frame(err); ok {
			s += " (" + path.Base(frame.Function) + ")"
		}

		ss = append(ss, s)
	}
	return strings.Join(ss, " - ")
}
Output:

first wrapper: base
first wrapper (error_example_test.go:28): base (error_example_test.go:27)
first wrapper - base
first wrapper (zerrors_test.ExampleSWrap) - base (zerrors_test.ExampleSWrap)

func Value

func Value(err error) error

Value returns the error contained at this particular node in the error chain. For non-wrapError errors, it returns itself. The return value is never a wrapError, and is only nil if the input is also nil.

func Wrap

func Wrap(inErr, outErr error) error

Wrap produces a wrapError using any error type. The first argument is the error being wrapped, the second is the error being added to it. If the first error is nil, Wrap does nothing and returns nil. If the second error is nil, it returns the first error. This should be avoided. Otherwise it returns a wrapError. If frame capture is enabled, it will get one at the caller of Wrap.

Example
package main

import (
	"errors"
	"fmt"
	"path"
	"strings"

	"github.com/JavierZunzunegui/zerrors"
)

type codeError struct {
	code int
}

func (e codeError) Error() string { return fmt.Sprintf("code=%d", e.code) }

func main() {
	err := zerrors.SNew("base")
	err = zerrors.Wrap(err, codeError{1})
	fmt.Println(err)                          // basic standard format
	fmt.Println(zerrors.Detail(err))          // detail standard format
	fmt.Println(basicCustomErrorFormat(err))  // basic custom format
	fmt.Println(detailCustomErrorFormat(err)) // detail custom format
}

// An example custom alternative to err.Error().
func basicCustomErrorFormat(err error) string {
	var ss []string
	for ; err != nil; err = errors.Unwrap(err) {
		ss = append(ss, zerrors.Value(err).Error())
	}
	return strings.Join(ss, " - ")
}

// An example custom alternative to zerrors.Detail(err).
func detailCustomErrorFormat(err error) string {
	var ss []string
	for ; err != nil; err = errors.Unwrap(err) {
		s := zerrors.Value(err).Error()

		if frame, ok := zerrors.Frame(err); ok {
			s += " (" + path.Base(frame.Function) + ")"
		}

		ss = append(ss, s)
	}
	return strings.Join(ss, " - ")
}
Output:

code=1: base
code=1 (error_example_test.go:48): base (error_example_test.go:47)
code=1 - base
code=1 (zerrors_test.ExampleWrap) - base (zerrors_test.ExampleWrap)

Types

This section is empty.

Directories

Path Synopsis
Package internal contains stateful global variables internal to zerrors.
Package internal contains stateful global variables internal to zerrors.
Package zmain allows setting of some global state afeecting how all zerrors are wrapped or serialised.
Package zmain allows setting of some global state afeecting how all zerrors are wrapped or serialised.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL