README
¶
errors
This is our own implementation of the Golang error
interface.
Why
Packages tend to use one of the following to return errors:
- Standard go
"errors"
package witherrors.New("...")
- Standard go
"fmt"
package withfmt.Errorf("...")
github.com/pkg/errors
package witherrors.New("...")
Of those three, the pkg/errors is by far the better option as it includes an error stack.
One could use pkg/errors, but:
- it does not offer much flexibility in how it displays errors
- its display is cluttered, with the wrong info (e.g. runtime stack and not the error handling stack)
- it does not display the up passing of errors in the stack unless you add the stack each time
- if you do add the stack each time, it dumps many copies of a stack and becomes cluttered
We write our own version of errors, to:
- always add the stack
- add the correct stack of where we raised/handled the error
- make error handling simpler (Wrapf(err) vs WithMessage(WithStack(err)))
- give us all the flexibility we need
- e.g. give us option to output JSON structured stack
For lots of detail on this, see comments in error_formats_test.go and run that test with go test -v error_formats_test.go
...
Usage
Get the package into your project:
go get gitlab.bob.co.za/bob-public-utils/bobgroup-go-utils
func New()
Fail with your own simple error message:
if !authenticated {
return errors.New("not authenticated")
}
func Errorf()
Fail with your own error using Printf style, e.g. when you detect an invalid value:
if limit < 0 {
return errors.Errorf("invalid limit %d<0", limit)
}
func Wrapf()
Fail when a called function returned an error:
if err := db.Select(query); err != nil {
return errors.Wrapf(err, "query failed on user=%s", username)
}
func HTTP()
Create an HTTP error, which is the same as others but includes and HTTP Status code:
if err := db.Select(query); err != nil {
return errors.HTTP(http.StatusNotFound, err, "cannot read users")
}
To retrieve the code, use err.Code() which returns integer value.
If you wrapped an HTTP error in another HTTP error, there may be different codes in the stack. The Code() method will dig down all the causes and return the lowest non-zero code value it finds. So StatusNotFound (404) will have precedence over StatusInsufficientStorage (507) It will return 0 when there are no codes in the error stack.
An error with HTTP code will also print the code in the stack, e.g.:
http-error_test.go:27: failed:
http-error_test.go(24): TestHTTPError() terrible mistake HTTP(404:Not Found), because
http-error_test.go(21): TestHTTPError() jissis this is bad! HTTP(507:Insufficient Storage), because
http-error_test.go(18): TestHTTPError() failed to get user HTTP(400:Bad Request), because
http-error_test.go(14): TestHTTPError() failed to connect to db
Refactoring exiting code
Replace all other errors package imports with this package:
import (
"gitlab.bob.co.za/bob-public-utils/bobgroup-go-utils/errors"
)
Refactor from standad go "errors" package:
- No change:
errors.New()
is still supported in this package
Refactor from "fmt" package:
- Replace
errors.Errorf("my message: %v", err)
witherrors.Wrapf(err, "my message")
so that the layers are preserved and not merged into a single text string.
Refactor from "github.com/pkg/errors":
- Replace
errors.WithStack(err)
witherrors.New(err)
. - Replace
return err
withreturn errors.Wrapf(err, "some message")
saying what failed as result of the err - Replace
errors.WithMessagef(err, "...", ...)
withreturn errors.Wrapf(err, "...", ...)
- Replace
errors.WithMessage(err, "...")
withreturn errors.Wrap(err, "...")
Formatting
Report an error with:
user,err := getUser(userName)
if err != nil {
log.Errorf("failed to get user %s: %+v", username, err)
...
}
Select the appropriate format:
%+c
in most cases to write the full compact error with file names and line numbers all on one line.%+v
is the same when you write to console over multipole lines%c
is full error on one line, without reference to source code, i.e. concatenate the error message of all levels e.g.A, because B, because C
, which you also get fromerr.Error()
.%v
is full error over multipole lines, without references to source code, e.g.
A, because
B, because
C
%s
is just the last error message.
JSON error:
Call err.Description()
to get a struct that can marshal to JSON for a complete dump, e.g.:
{
"error": "login failed",
"source": {
"package": "gitlab.bob.co.za/bob-public-utils/bobgroup-go-utils/errors/errors_test",
"file": "errors_test.go",
"line": 18,
"function": "TestErrorFormatting"
},
"cause": {
"error": "failed to find account",
"source": {
"package": "gitlab.bob.co.za/bob-public-utils/bobgroup-go-utils/errors/errors_test",
"file": "errors_test.go",
"line": 17,
"function": "TestErrorFormatting"
},
"cause": {
"error": "query failed",
"source": {
"package": "gitlab.bob.co.za/bob-public-utils/bobgroup-go-utils/errors/errors_test",
"file": "errors_test.go",
"line": 16,
"function": "TestErrorFormatting"
},
"cause": {
"error": "you have problem in your SQL near xxx"
}
}
}
}
func Caller()
This function can be used also for logging to determine the caller from the runtime stack. It takes a skip value to skip a few levels of the stack, making it flexible to be called in various wrapper functions, without reporting the wrappers.
func Stack()
This function is similar to Caller() but reports an array of callers, not only one.
func Is()
You can compare a message with errors.Is() to some error specification. It will look at the error or any cause to match the spec. The spec is the error message.
Documentation
¶
Index ¶
- func AWSErrorWithoutExceptionCode(err error) error
- func Error(message string) error
- func Errorf(format string, args ...interface{}) error
- func HTTP(code int, err error, format string, args ...interface{}) error
- func HTTPCode(err error) int
- func HTTPCodeOnly(code int) error
- func HTTPWithError(code int, err error) error
- func HTTPWithMsg(code int, format string, args ...interface{}) error
- func Is(e1, e2 error) bool
- func IsRetryableError(err error) bool
- func New(message string) error
- func Wrap(err error, msg string) error
- func Wrapf(err error, format string, args ...interface{}) error
- type Caller
- type CallerInfo
- type CustomError
- func (err CustomError) Cause() error
- func (err CustomError) Code() int
- func (err CustomError) Description() Description
- func (err CustomError) Error() string
- func (err CustomError) Format(s fmt.State, v rune)
- func (err CustomError) Formatted(opts FormattingOptions) string
- func (err CustomError) Is(specificError error) bool
- type Description
- type ErrorWithCause
- type ErrorWithIs
- type FormattingOptions
- type ICaller
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func AWSErrorWithoutExceptionCode ¶ added in v1.95.0
func HTTPCodeOnly ¶
func HTTPWithError ¶
func HTTPWithMsg ¶
func IsRetryableError ¶
Types ¶
type Caller ¶
type Caller struct {
// contains filtered or unexported fields
}
e.g.:
func (Caller) Info ¶
func (c Caller) Info() CallerInfo
func (Caller) Package ¶
with Function: "github.com/go-msvc/ms_test.TestCaller" return "github.com/go-msvc/ms_test"
func (Caller) PackageFile ¶
return "github.com/go-msvc/ms_test/my_test.go"
type CallerInfo ¶
type CallerInfo struct { Package string `json:"package"` File string `json:"file"` Line int `json:"line"` Function string `json:"function"` }
func Stack ¶
func Stack(skip int) []CallerInfo
type CustomError ¶
type CustomError struct {
// contains filtered or unexported fields
}
CustomError implements the following interfaces:
error github.com/pkg/errors: Cause
func (CustomError) Cause ¶
func (err CustomError) Cause() error
implement github.com/pkg/errors: Cause
func (CustomError) Code ¶
func (err CustomError) Code() int
func (CustomError) Description ¶
func (err CustomError) Description() Description
func (CustomError) Formatted ¶
func (err CustomError) Formatted(opts FormattingOptions) string
func (CustomError) Is ¶
func (err CustomError) Is(specificError error) bool
Is() compares the message string of this or any cause to match the specified error message
type Description ¶
type Description struct { Message string `json:"error"` Source *CallerInfo `json:"source,omitempty"` Cause *Description `json:"cause,omitempty"` }
type ErrorWithCause ¶
extends default golang error interface