errors

package
v1.205.0 Latest Latest
Warning

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

Go to latest
Published: Apr 16, 2024 License: BSD-3-Clause Imports: 9 Imported by: 0

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 with errors.New("...")
  • Standard go "fmt" package with fmt.Errorf("...")
  • github.com/pkg/errors package with errors.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) with errors.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) with errors.New(err).
  • Replace return err with return errors.Wrapf(err, "some message") saying what failed as result of the err
  • Replace errors.WithMessagef(err, "...", ...) with return errors.Wrapf(err, "...", ...)
  • Replace errors.WithMessage(err, "...") with return 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 from err.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

Constants

This section is empty.

Variables

This section is empty.

Functions

func AWSErrorExceptionCode added in v1.99.0

func AWSErrorExceptionCode(err error) string

func AWSErrorWithoutExceptionCode added in v1.95.0

func AWSErrorWithoutExceptionCode(err error) error

func HTTPCode

func HTTPCode(err error) int

func Is

func Is(e1, e2 error) bool

func IsRetryableError

func IsRetryableError(err error) bool

func IsRetryableErrorOrShouldFail added in v1.119.0

func IsRetryableErrorOrShouldFail(err error) (shouldRetry bool, shouldFail bool)

Types

type Caller

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

e.g.:

func GetCaller

func GetCaller(skip int) Caller

func NewCaller

func NewCaller(pkgDotFunc string, file string, line int) Caller

func (Caller) File

func (c Caller) File() string

return full file name on system where code is built...

func (Caller) Format

func (caller Caller) Format(f fmt.State, c rune)

func (Caller) Function

func (c Caller) Function() string

with Function: "github.com/go-msvc/ms_test.TestCaller" return "TestCaller"

func (Caller) Info

func (c Caller) Info() CallerInfo

func (Caller) Line

func (c Caller) Line() int

func (Caller) Package

func (c Caller) Package() string

with Function: "github.com/go-msvc/ms_test.TestCaller" return "github.com/go-msvc/ms_test"

func (Caller) PackageFile

func (c Caller) PackageFile() string

return "github.com/go-msvc/ms_test/my_test.go"

func (Caller) String

func (c Caller) String() string

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 Error

func Error(message string) *CustomError

func Errorf

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

func HTTP

func HTTP(code int, err error, format string, args ...interface{}) *CustomError

func HTTPCodeOnly

func HTTPCodeOnly(code int) *CustomError

func HTTPWithError

func HTTPWithError(code int, err error) *CustomError

func HTTPWithMsg

func HTTPWithMsg(code int, format string, args ...interface{}) *CustomError

func ToCustomError added in v1.187.0

func ToCustomError(err error) *CustomError

func Wrap

func Wrap(err error, msg string) *CustomError

func Wrapf

func Wrapf(err error, format string, args ...interface{}) *CustomError

func (*CustomError) BypassRaygun added in v1.185.0

func (err *CustomError) BypassRaygun() *CustomError

func (*CustomError) BypassSQSError added in v1.185.0

func (err *CustomError) BypassSQSError() *CustomError

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) Error

func (err *CustomError) Error() string

implement interface error:

func (*CustomError) Format

func (err *CustomError) Format(s fmt.State, v rune)

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

func (*CustomError) ShouldBypassRaygun added in v1.185.0

func (err *CustomError) ShouldBypassRaygun() bool

func (*CustomError) ShouldBypassSQSError added in v1.185.0

func (err *CustomError) ShouldBypassSQSError() bool

type Description

type Description struct {
	Message string       `json:"error"`
	Source  *CallerInfo  `json:"source,omitempty"`
	Cause   *Description `json:"cause,omitempty"`
}

type ErrorWithCause

type ErrorWithCause interface {
	error
	Cause() error
	Code() int
}

extends default golang error interface

type ErrorWithIs

type ErrorWithIs interface {
	error
	Is(specificError error) bool
}

type FormattingOptions

type FormattingOptions struct {
	Causes   bool
	NewLines bool
	Source   bool
}

type ICaller

type ICaller interface {
	fmt.Stringer
	Package() string
	Function() string
	File() string
	Line() int
}

Jump to

Keyboard shortcuts

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