failure

package module
Version: v1.1.2 Latest Latest
Warning

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

Go to latest
Published: Oct 25, 2022 License: MIT Imports: 9 Imported by: 64

README

failure

CircleCI Go Reference Go Report Card codecov

Package failure provides errors utilities for your application errors.

  • Automatically generate awesome err.Error message for developers.
  • Flexible error messages for end users.
  • Powerful and readable stack trace.
  • Error context, such as function parameter, with key-value data.
  • Extensible error chain.

Usage

At first, define error codes for your application.

const (
	NotFound failure.StringCode = "NotFound"
	InvalidArgument failure.StringCode = "InvalidArgument"
	Internal failure.StringCode = "Internal"
)

Using failure.New, return an error with error code.

return failure.New(NotFound)

Handle the error with failure.Is and translate it into another error code with failure.Translate.

if failure.Is(err, NotFound) {
	return failure.Translate(err, Internal)
}

If you want to just return the error, use failure.Wrap.

if err != nil {
	return failure.Wrap(err)
}

An error context and message for end user can be attached.

func Foo(a, b string) error {
	return failure.New(InvalidArgument, 
		failure.Context{"a": a, "b": b},
		failure.Message("Given parameters are invalid!!"),
	)
}

Awesome error message for developers.

func main() {
	err := Bar()
	fmt.Println(err)
	fmt.Println("=====")
	fmt.Printf("%+v\n", err)
}

func Bar() error {
	err := Foo("hello", "world")
	if err != nil {
		return failure.Wrap(err)
	}
	return nil
}
main.Bar: main.Foo: a=hello b=world: Given parameters are invalid!!: code(InvalidArgument)
=====
[main.Bar] /tmp/sandbox615088634/prog.go:25
[main.Foo] /tmp/sandbox615088634/prog.go:31
    a = hello
    b = world
    message("Given parameters are invalid!!")
    code(InvalidArgument)
[CallStack]
    [main.Foo] /tmp/sandbox615088634/prog.go:31
    [main.Bar] /tmp/sandbox615088634/prog.go:23
    [main.main] /tmp/sandbox615088634/prog.go:16
    [runtime.main] /usr/local/go-faketime/src/runtime/proc.go:204
    [runtime.goexit] /usr/local/go-faketime/src/runtime/asm_amd64.s:1374

package.FunctionName like main.Bar and main.Foo is automatically added to error message. With %+v format, it prints the detailed error chain + the call stack of the first error.

Full Example for HTTP Server

Try it on The Go Playground!

Or click on the below to see the code.

package main

import (
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"net/http/httputil"

	"github.com/morikuni/failure"
)

// error codes for your application.
const (
	NotFound  failure.StringCode = "NotFound"
	Forbidden failure.StringCode = "Forbidden"
)

func GetACL(projectID, userID string) (acl interface{}, e error) {
	notFound := true
	if notFound {
		return nil, failure.New(NotFound,
			failure.Context{"project_id": projectID, "user_id": userID},
		)
	}
	return nil, failure.Unexpected("unexpected error")
}

func GetProject(projectID, userID string) (project interface{}, e error) {
	_, err := GetACL(projectID, userID)
	if err != nil {
		if failure.Is(err, NotFound) {
			return nil, failure.Translate(err, Forbidden,
				failure.Message("no acl exists"),
				failure.Context{"additional_info": "hello"},
			)
		}
		return nil, failure.Wrap(err)
	}
	return nil, nil
}

func Handler(w http.ResponseWriter, r *http.Request) {
	_, err := GetProject(r.FormValue("project_id"), r.FormValue("user_id"))
	if err != nil {
		HandleError(w, err)
		return
	}
}

func getHTTPStatus(err error) int {
	c, ok := failure.CodeOf(err)
	if !ok {
		return http.StatusInternalServerError
	}
	switch c {
	case NotFound:
		return http.StatusNotFound
	case Forbidden:
		return http.StatusForbidden
	default:
		return http.StatusInternalServerError
	}
}

func getMessage(err error) string {
	msg, ok := failure.MessageOf(err)
	if ok {
		return msg
	}
	return "Error"
}

func HandleError(w http.ResponseWriter, err error) {
	w.WriteHeader(getHTTPStatus(err))
	io.WriteString(w, getMessage(err))

	fmt.Println("============ Error ============")
	fmt.Printf("Error = %v\n", err)

	code, _ := failure.CodeOf(err)
	fmt.Printf("Code = %v\n", code)

	msg, _ := failure.MessageOf(err)
	fmt.Printf("Message = %v\n", msg)

	cs, _ := failure.CallStackOf(err)
	fmt.Printf("CallStack = %v\n", cs)

	fmt.Printf("Cause = %v\n", failure.CauseOf(err))

	fmt.Println()
	fmt.Println("============ Detail ============")
	fmt.Printf("%+v\n", err)
	// [main.GetProject] /go/src/github.com/morikuni/failure/example/main.go:36
	//     message("no acl exists")
	//     additional_info = hello
	//     code(Forbidden)
	// [main.GetACL] /go/src/github.com/morikuni/failure/example/main.go:21
	//     project_id = 123
	//     user_id = 456
	//     code(NotFound)
	// [CallStack]
	//     [main.GetACL] /go/src/github.com/morikuni/failure/example/main.go:21
	//     [main.GetProject] /go/src/github.com/morikuni/failure/example/main.go:33
	//     [main.Handler] /go/src/github.com/morikuni/failure/example/main.go:47
	//     [http.HandlerFunc.ServeHTTP] /usr/local/go/src/net/http/server.go:1964
	//     [http.(*ServeMux).ServeHTTP] /usr/local/go/src/net/http/server.go:2361
	//     [http.serverHandler.ServeHTTP] /usr/local/go/src/net/http/server.go:2741
	//     [http.(*conn).serve] /usr/local/go/src/net/http/server.go:1847
	//     [runtime.goexit] /usr/local/go/src/runtime/asm_amd64.s:1333
}

func main() {
	req := httptest.NewRequest(http.MethodGet, "/?project_id=aaa&user_id=111", nil)
	rec := httptest.NewRecorder()
	Handler(rec, req)

	res, _ := httputil.DumpResponse(rec.Result(), true)
	fmt.Println("============ Dump ============")
	fmt.Println(string(res))
}

Documentation

Overview

Package failure provides an error represented as error code and extensible error interface with wrappers.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CauseOf

func CauseOf(err error) error

CauseOf returns a most underlying error of the err.

func Custom added in v0.7.0

func Custom(err error, wrappers ...Wrapper) error

Custom is the general error wrapping constructor. It just wraps err with given wrappers.

func Is added in v0.7.0

func Is(err error, codes ...Code) bool

Is checks whether an error code from the err is any of given code.

func MarkUnexpected added in v0.12.1

func MarkUnexpected(err error, wrappers ...Wrapper) error

MarkUnexpected wraps err and preventing propagation of error code from underlying error. It is used where an error can be returned but expecting it does not happen. The returned error does not return error code from function CodeOf.

func MessageOf

func MessageOf(err error) (string, bool)

MessageOf extracts a message from the err.

func New

func New(code Code, wrappers ...Wrapper) error

New creates an error from error code.

func Trace added in v0.14.0

func Trace(err error, vs Tracer)

func Translate

func Translate(err error, code Code, wrappers ...Wrapper) error

Translate translates the err to an error with given code. It wraps the error with given wrappers, and automatically add call stack and formatter.

func Unexpected added in v0.9.0

func Unexpected(msg string, wrappers ...Wrapper) error

Unexpected creates an error from message without error code. The returned error should be kind of internal or unknown error.

func Wrap

func Wrap(err error, wrappers ...Wrapper) error

Wrap wraps err with given wrappers, and automatically add call stack and formatter.

Types

type CallStack

type CallStack interface {
	// HeadFrame returns a frame of where call stack is created.
	// This is same as Frames()[0], but uses memory more efficiently.
	HeadFrame() Frame
	// Frames returns entire frames of the call stack.
	Frames() []Frame
}

CallStack represents a call stack. The call stack includes information where the occurred and how the function was called.

func CallStackOf

func CallStackOf(err error) (CallStack, bool)

CallStackOf extracts a call stack from the err. Returned call stack is for the most deepest place (appended first).

func Callers

func Callers(skip int) CallStack

Callers returns a call stack for the current state.

func NewCallStack added in v0.9.4

func NewCallStack(pcs []uintptr) CallStack

NewCallStack returns call stack from program counters. You can use Callers for usual usage.

type Code

type Code interface {
	// ErrorCode returns an error code in string representation.
	ErrorCode() string
}

Code represents an error code of an error. The code should be able to be compared by == operator. Basically, it should to be defined as constants.

You can also define your own code type instead of using StringCode type, when you want to distinguish errors by type for some purpose (e.g. define code type for each package like user, item, auth etc).

func CodeOf

func CodeOf(err error) (Code, bool)

CodeOf extracts an error code from the err. The first error code found in the err is returned, but if Unexpected interface is detected before the code is found, it behaves as if there is no code found.

type Context added in v0.11.0

type Context map[string]string

Context is a key-value data which describes the how the error occurred for debugging purpose. You must not use context data as a part of your application logic. Just print it. If you want to extract Context from error for printing purpose, you can define an interface with method `GetContext() Context` and use it with iterator, like other extraction functions (see: MessageOf).

func (Context) Context added in v1.1.0

func (c Context) Context() Context

func (Context) WrapError added in v0.11.0

func (c Context) WrapError(err error) error

WrapError implements the Wrapper interface.

type Contexter added in v1.1.0

type Contexter interface {
	Context() Context
}

Contexter is an interface to be used with errors.As.

type Frame added in v0.2.0

type Frame interface {
	// Path returns a absolute path to the file.
	Path() string
	// File returns a file name.
	File() string
	// Line returns a line number in the file.
	Line() int
	// Func returns a function name.
	Func() string
	// Pkg returns a package name of the function.
	Pkg() string
	// PkgPath returns a path-qualified package name of the function.
	PkgPath() string
	// PC returns a program counter of this frame.
	PC() uintptr
}

Frame represents a stack frame.

type Iterator added in v0.7.0

type Iterator struct {
	// contains filtered or unexported fields
}

Iterator is designed to iterate wrapped errors with for loop.

func NewIterator added in v0.7.0

func NewIterator(err error) *Iterator

NewIterator creates an iterator for the err.

func (*Iterator) As added in v0.12.0

func (i *Iterator) As(x interface{}) bool

As tries to extract data from current error. It returns true if the current error implemented As and it returned true.

func (*Iterator) Error added in v0.7.0

func (i *Iterator) Error() error

Error returns current error.

func (*Iterator) Next added in v0.7.0

func (i *Iterator) Next() bool

Next tries to unwrap an error and returns whether the next error is present. Since this method updates internal state of the iterator, should be called only once per iteration.

type Message added in v0.5.0

type Message string

Message is a wrapper which appends message to an error.

func Messagef added in v0.9.0

func Messagef(format string, args ...interface{}) Message

Messagef returns Message with formatting.

func (Message) Message added in v1.1.0

func (m Message) Message() string

func (Message) String added in v0.12.0

func (m Message) String() string

String returns underlying string message.

func (Message) WrapError added in v0.12.0

func (m Message) WrapError(err error) error

WrapError implements Wrapper interface.

type Messenger added in v1.1.0

type Messenger interface {
	Message() string
}

Messenger is an interface to be used with errors.As.

type StringCode added in v0.2.0

type StringCode string

StringCode represents an error Code in string.

func (StringCode) ErrorCode added in v0.2.0

func (c StringCode) ErrorCode() string

ErrorCode implements the Code interface.

type StringTracer added in v0.14.0

type StringTracer []string

func (*StringTracer) Push added in v0.14.0

func (st *StringTracer) Push(v interface{})

type Tracer added in v0.14.0

type Tracer interface {
	Push(v interface{})
}

type Wrapper added in v0.7.0

type Wrapper interface {
	// WrapError should wrap given err to append some
	// capability to the error.
	WrapError(err error) error
}

Wrapper interface is used by constructor functions.

func WithCallStackSkip added in v0.7.0

func WithCallStackSkip(skip int) Wrapper

WithCallStackSkip appends a call stack to the err skipping first N frames. You don't have to use this directly, unless using function Custom.

func WithCode added in v0.9.0

func WithCode(code Code) Wrapper

WithCode appends code to an error. You don't have to use this directly, unless using function Custom. Basically, you can use function Translate instead of this.

func WithFormatter added in v0.7.0

func WithFormatter() Wrapper

WithFormatter appends an error formatter to the err.

%v+: Print trace for each place, and deepest call stack.
%#v: Print raw structure of the error.
others (%s, %v): Same as err.Error().

You don't have to use this directly, unless using function Custom.

func WithUnexpected added in v0.13.0

func WithUnexpected() Wrapper

WithUnexpected wraps the err to mark it is unexpected. You don't have to use this directly, unless using function Custom. Please use Unexpected or MarkUnexpected.

type WrapperFunc added in v0.7.0

type WrapperFunc func(err error) error

WrapperFunc is an adaptor to use function as the Wrapper interface.

func (WrapperFunc) WrapError added in v0.7.0

func (f WrapperFunc) WrapError(err error) error

WrapError implements the Wrapper interface.

Jump to

Keyboard shortcuts

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