errors

package module
v0.1.1 Latest Latest
Warning

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

Go to latest
Published: Feb 12, 2019 License: BSD-2-Clause Imports: 4 Imported by: 0

README

Docs

See https://godoc.org/github.com/pashaosipyants/errors

License

BSD-2-Clause, see LICENSE file.

An initial point of this project was forking https://github.com/pkg/errors. It's license is in LICENSE - fork file. (BSD-2-Clause as well)

Documentation

Overview

Package errors provides detailed stack-traced description for error type and exception(handle-check) style handling

This package has two goals.

1. Provide convenient error type which has error code to easily distinguish errors, which can be extended by additional messages(annotations here) while error flows through the program, can be printed with stack trace.

2. Provide convenient way to handle errors in exception style.

With the first point everything is clear, let's talk a little about second. Go's error handling idiom usually looks like

x, err := DoSmth()
if err != nil {
    logger.Error(err)
    // may be some err processing
    return err
}

It is very "holy war" question which way to handle errors, exception like or "returning err" like, is better. Everyone chooses himself which way is more appropriate to him. But doubtlessly "returning err" style has some disadvantages.

1. Error handling logic is mixed with "business" logic so it shadows it

2. Code repetitions, which can cause a bug

3. Verbosity

That's why Go 2 draft https://go.googlesource.com/proposal/+/master/design/go2draft.md contains new Handle-Check error handling approach, which is quite similar to exception approach. This package provides similar to this Handle-Check mechanism, based on panics. Details are below =)

Errors

All errors created by this package are returned as golang native error interface. Underlying type is immutable, so one can't change smth in already existing error, but can use wrap functions, which create new one based on underlying. New, Errorf, Codef - creates new error. Wrap, WrapAnnotated - wrap existing.

Error codes

Error can be created with error code, which has interface{} type. Error code can be obtained from error by ErrCode function.

err := errors.New("description", 1)
if errors.ErrCode(err) == 1 {
  fmt.Println("Hooray, it works!")
}

Missing error code in creation is equal to nil error code.

err := errors.New("description")
if errors.ErrCode(err) == nil {
  fmt.Println("Hooray, it works!")
}

When one wraps error without providing error code, it is preserved. Otherwise - overridden.

err := errors.New("description", 1)
err = errors.Wrap(err)
if errors.ErrCode(err) == 1 {
  fmt.Println("Hooray, it works!")
}
err = errors.Wrap(err, 2)
if errors.ErrCode(err) == 2 {
  fmt.Println("Hooray, it still works!")
}

Annotations

With WrapAnnotated one can add additional messages to an error. Annotations are printed in stacktrace, bound to corresponding function(where it was added). If annotation was added out of functions in stacktrace, it will be printed in separate section.

Format

%v modifier will print full info, with stack trace and annotations. %s only error message. %q only error message quoted.

E.g. with %v:

ERROR: connection failed
ERR CODE: connection_failed

github.com/pashaosipyants/errors/example_auxiliary.SaveTaskToDbMockConnectionError
	D:/work/go/src/github.com/pashaosipyants/errors/example_auxiliary/example_auxiliary.go:19
github.com/pashaosipyants/errors/example_auxiliary.CreateTaskInitedByUser1
	D:/work/go/src/github.com/pashaosipyants/errors/example_auxiliary/example_auxiliary.go:63
ANNOTATIONS:
Inited by user 1
github.com/pashaosipyants/errors_test.apiCreateTask
	D:/work/go/src/github.com/pashaosipyants/errors/example_compehensive_test.go:69
github.com/pashaosipyants/errors_test.Example.func1
	D:/work/go/src/github.com/pashaosipyants/errors/example_compehensive_test.go:40
github.com/pashaosipyants/errors_test.Example
	D:/work/go/src/github.com/pashaosipyants/errors/example_compehensive_test.go:44
testing.runExample
	C:/Go/src/testing/example.go:121
testing.runExamples
	C:/Go/src/testing/example.go:45
testing.(*M).Run
	C:/Go/src/testing/testing.go:1035
main.main
	_testmain.go:70
runtime.main
	C:/Go/src/runtime/proc.go:201
runtime.goexit
	C:/Go/src/runtime/asm_amd64.s:1333

Cause

To get underlying error use Cause func.

err := errors.Wrap(io.EOF)
if reflect.DeepEqual(err, io.EOF) {
  fmt.Println("Hooray, it works!")
}

Skipstack management

All creation & wrapping functions have their duplicates with _skipstack suffix and skip argument. With those functions one can specify correct first stack frame to print in stack trace. skip==0 means starting from caller of this function. It can be useful for example to skip stack frames of wrapper object, if you want your own wrapper of this package.

type MyErr struct {
  error
  X AddInfo
}

func NewMyErr(message string, errcode ...interface{}, x AddInfo) MyErr {
  return MyErr{New_skipstack(message, 1, errcode...), x}
}

Handle check mechanism

example:

defer errors.Handler(func(err errors.Handleable) {
  log.Error(err)
  switch err.ErrCode() {
    case 1:
      repairError1()
    case 2:
      repairError2()
    default:
      panic("Unknown err code")
  }
})

err := task1()
errors.Check(err, 1)

x := twoPlusTwo()
errors.Check(x == 4, 2)
Example

This is comprehensive, pretending to be close to real-life example of using this package. It's easier to see it in code, but if you use godoc, please, notice https://godoc.org/github.com/pashaosipyants/errors/example_auxiliary package, which is used here.

Imagine there is an api to create a task that executes another service. Besides user of this api wants to hold info whether this task is already done. This api, ofc, can return error. E.g. certain task may be already created. If so, error should report whether it is done or not.

package main

import (
	"fmt"
	"github.com/pashaosipyants/errors"
	"github.com/pashaosipyants/errors/example_auxiliary"
)

// This is comprehensive, pretending to be close to real-life example of using this package.
// It's easier to see it in code, but if you use godoc, please, notice
// https://godoc.org/github.com/pashaosipyants/errors/example_auxiliary package,
// which is used here.
//
// Imagine there is an api to create a task that executes another service.
// Besides user of this api wants to hold info whether this task is already done.
// This api, ofc, can return error. E.g. certain task may be already created.
// If so, error should report whether it is done or not.
func main() {
	// loop to work out different cases
	for i := 0; i < 4; i++ {
		// func is smth like try block here
		func() {
			// smth like catch block
			defer errors.Handler(func(err errors.Handleable) {
				switch errors.ErrCode(err) {
				case errcode_apicreatetaskfailed:
					fmt.Printf("%s\n\n\n", err) // log
					// may be some specific actions
				case errcode_apiuserloginfailed:
					// may be some specific actions
					panic("Assertion failed") // but in our example can't be here
				default:
					panic("Assertion failed")
				}
			})

			err := apiUserLogin()
			errors.Check(err, errcode_apiuserloginfailed)

			err = apiCreateTask(i)
			errors.Check(err, errcode_apicreatetaskfailed) // override errcode

			fmt.Println("Success!!!") // log
		}()
	}

}

const errcode_apicreatetaskfailed = "api_create_task_failed"
const errcode_apiuserloginfailed = "api_user_login_failed"

func apiCreateTask(i int) (reterr error) {
	defer errors.Handler(func(err errors.Handleable) {
		switch errors.ErrCode(err) {
		case example_auxiliary.ErrCode_ConnectionFailed, example_auxiliary.ErrCode_TaskAlreadyExistButNotDone:
			fmt.Print(err, "\n\n\n\n") // log
			reterr = err
		case example_auxiliary.ErrCode_TaskAlreadyExistAndDone:
			fmt.Print(err, "\n\n\n\n") // log
			// do some specific logic - e.g. mark task in db as done
			reterr = err
		default:
			panic("Assertion failed")
		}
	})

	err := example_auxiliary.CreateTaskInitedByUser1(i)
	errors.Check(err)

	return nil
}

// pretends to be always success
func apiUserLogin() error {
	// some work

	return nil
}
Output:

wrong output specially to make this function be executed and see output of this example

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Cause

func Cause(err error) error

If it is possible, gets underlying error wrapped with this package's error type. Otherwise returns err itself.

func Check

func Check(err error, errcode ...interface{})

Check panics with an error, first wrapping it with errcode. One can handle this panic defering Handler func.

func CheckIf

func CheckIf(ifErr bool, err error, errcode ...interface{})

CheckIf panics with an error if ifErr is true, first wrapping err it with errcode. One can handle this panic defering Handler func.

func CheckIfNew

func CheckIfNew(ifErr bool, message string, errcode ...interface{})

CheckIfNew panics with an error if ifErr is true. error is made from message and errcode. One can handle this panic defering Handler func.

func Codef

func Codef(errcode interface{}, format string, args ...interface{}) error

Codef returns error with formatted message and specified error code.

func Codef_skipstack

func Codef_skipstack(skip int, errcode interface{}, format string, args ...interface{}) error

see https://godoc.org/github.com/pashaosipyants/errors/#hdr-Skipstack_management

func ErrCode

func ErrCode(err error) interface{}

If it is possible, gets errcode of the error. Otherwise returns nil.

func Errorf

func Errorf(format string, args ...interface{}) error

Errorf returns error with formatted message and without error code.

func Errorf_skipstack

func Errorf_skipstack(skip int, format string, args ...interface{}) error

see https://godoc.org/github.com/pashaosipyants/errors/#hdr-Skipstack_management

func Handler

func Handler(handle func(err Handleable))

defer Handle and provide handler to process panics made by Check...(). Panics with type different to this package's one are just forwarded.

func New

func New(message string, errcode ...interface{}) error

New returns error with error code, if specified.

errcode is optional param. First of variadic parameters is used, else are ignored.

func New_skipstack

func New_skipstack(message string, skip int, errcode ...interface{}) error

see https://godoc.org/github.com/pashaosipyants/errors/#hdr-Skipstack_management

func Wrap

func Wrap(err error, errcode ...interface{}) error

Wrap returns error with underlying err and error code, if specified. If err is nil returns nil. If err already has error code(it means it was created earlier by one of this package's functions) and errcode is specified, it is overridden.

errcode is optional param. First of variadic parameters is used, else are ignored.

func WrapAnnotated

func WrapAnnotated(err error, annotation string, errcode ...interface{}) error

WrapAnnotated returns annotated error with underlying err and error code, if specified. If err is nil returns nil. If err already has error code(it means it was created earlier by one of this package's functions) and errcode is specified, it is overridden. See https://godoc.org/github.com/pashaosipyants/errors/#hdr-Annotations to learn about annotations.

errcode is optional param. First of variadic parameters is used, else are ignored.

func WrapAnnotated_skipstack

func WrapAnnotated_skipstack(skip int, err error, annotation string, errcode ...interface{}) error

see https://godoc.org/github.com/pashaosipyants/errors/#hdr-Skipstack_management

func Wrap_skipstack

func Wrap_skipstack(skip int, err error, errcode ...interface{}) error

see https://godoc.org/github.com/pashaosipyants/errors/#hdr-Skipstack_management

Types

type Handleable

type Handleable interface {
	Error() string
	ErrCode() interface{}
	Cause() error
}

Type to handle Check...() produced panic.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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