errors

package module
v0.0.4 Latest Latest
Warning

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

Go to latest
Published: Sep 12, 2022 License: MIT Imports: 9 Imported by: 16

README

Errors Logo

made-with-Go Go Report Card Maintainability Test codecov go.dev reference

🫠 Errors

A drop-in replacement for Go errors, with some added sugar! Error handling in Go made easy with codes, messages and more. Failure is your domain!

Overview

  • ✅ A humble selection of errors codes that touches most bases.
  • ✅ User friendly error messages for returning via an API for example.
  • ✅ Operation support (Struct.Method) naming convention.
  • ✅ Retrieve call stacks as preformatted or as a string slice.
  • ✅ Generate HTTP response codes from all error types.
  • ✅ Extremely lightweight with no external dependencies.

Why?

Errors are as important in your domain and application as the entities such as a User or Database. They should be treated as individual types. Not only do they give a clear meaning to the users of the application if something goes wrong, they can save hours of debugging time when used effectively.

Coupled with consistent and effective use of a logging package, we are able to tell if something goes wrong, where it went wrong, how it went wrong and.

Installation

go get -u github.com/ainsleyclark/errors

How to use

See below on some common examples on how to use the error package.

The Error Type

The Error struct below describes an application error that's returned when using the New...() constructors such as NewInternal(). See below on more detail on what each field contains.

// Error defines a standard application error.
type Error struct {
	// The application error code.
	Code string `json:"code" bson:"code"`
	// A human-readable message to send back to the end user.
	Message string `json:"message" bson:"message"`
	// Defines what operation is currently being run.
	Op string `json:"operation" bson:"op"`
	// The error that was returned from the caller.
	Err error `json:"error" bson:"err"`
}
Returning

Below is an example of returning a formatted Error type in a database call for obtaining a singular user. If no rows are found, we return a NOTFOUND code. Likewise if there was an error executing the SQL query, we return a NOTFOUND code with user-friendly messages.

func (s *UserStore) Find(ctx context.Context, schema string, id int64) (core.User, error) {
	const op = "UserStore.Find"

	q := "SELECT from users WHERE ID = ? LIMIT 1"

	var out core.User
	err := s.DB().GetContext(ctx, &out, q.Build(), id)
	if err == sql.ErrNoRows {
		return core.User{}, errors.NewNotFound(err, fmt.Sprintf("Error obtaining User with the ID: %d", id), op)
	} else if err != nil {
		return core.User{}, errors.NewInternal(err, "Error executing SQL query", op)
	}

	return out, nil
}
Output

Let's assume that an SQL error occurred during the execution of the query and no rows were returned. Without any context of the error, we simply get:

syntax error near SELECT

However, by calling err.Error() on our wrapped error, it will return:

<internal> /Users/me/project/store/users.go:27 - UserStore.Find: syntax error near SELECT, Error executing SQL query

Now we know exactly where the error occurred, why it occurred and what file line and method.

Checking Types

The package comes built in with handy functions for obtaining messages, codes and casting to the Error type, see below for some examples.

To Error
e := errors.New("error")
err := errors.ToError(e)
Obtaining a message
err := errors.NewInternal(errors.New("error"), "My Message", "Operation")
msg := errors.Message(err)
fmt.Println(msg) // Output - "My Message"
Obtaining an error code
err := errors.NewInternal(errors.New("error"), "My Message", "Operation")
code := errors.Code(err)
fmt.Println(code) // Output - "internal"

Available Error Codes

Below is a list of available error codes within the errors package. It's tempting to build fine-grained error codes, but it's a lot easier to manage more generic codes. The codes below are a good start to set off on, if you feel there is one missing, please open a pull request.

Code Value Notes
CONFLICT "conflict" An action cannot be performed.
INTERNAL "internal" Error within the application.
INVALID "invalid" Validation failed.
NOTFOUND "not_found" Entity does not exist.
UNKNOWN "unknown" Application unknown error.
MAXIMUMATTEMPTS "maximum_attempts" More than allowed action.
EXPIRED "expired" Subscription expired.

Benchmarks

Ran on 19/05/2022

$ go version
go version go1.18.1 darwin/amd64

$ go test -benchmem -bench .
goos: darwin
goarch: amd64
pkg: github.com/ainsleyclark/errors
cpu: AMD Ryzen 7 5800X 8-Core Processor
BenchmarkNew-16                          1000000              1026 ns/op            1320 B/op          6 allocs/op
BenchmarkNewInternal-16                  1000000              1023 ns/op            1320 B/op          6 allocs/op
BenchmarkError_Error-16                  6582385               180.6 ns/op           464 B/op          4 allocs/op
BenchmarkError_Code-16                  637953824                1.883 ns/op           0 B/op          0 allocs/op
BenchmarkError_Message-16               628838649                1.906 ns/op           0 B/op          0 allocs/op
BenchmarkError_ToError-16               705541435                1.699 ns/op           0 B/op          0 allocs/op
BenchmarkError_HTTPStatusCode-16        1000000000               0.6282 ns/op          0 B/op          0 allocs/op

Contributing

Please feel free to make a pull request if you think something should be added to this package!

Credits

Shout out to the incredible Maria Letta for her excellent Gopher illustrations.

Licence

Code Copyright 2022 Errors. Code released under the MIT Licence.

Documentation

Index

Constants

View Source
const (
	// CONFLICT - An action cannot be performed.
	CONFLICT = "conflict"
	// INTERNAL - Error within the application.
	INTERNAL = "internal"
	// INVALID - Validation failed.
	INVALID = "invalid"
	// NOTFOUND - Entity does not exist.
	NOTFOUND = "not_found"
	// UNKNOWN - Application unknown error.
	UNKNOWN = "unknown"
	// MAXIMUMATTEMPTS - More than allowed action.
	MAXIMUMATTEMPTS = "maximum_attempts"
	// EXPIRED - Subscription expired.
	EXPIRED = "expired"
)

Application error codes.

Variables

View Source
var (
	// DefaultCode is the default code returned when
	// none is specified.
	DefaultCode = INTERNAL
	// GlobalError is a general message when no error message
	// has been found.
	GlobalError = "An error has occurred."
)

Functions

func As

func As(err error, target any) bool

As calls the stdlib errors.As.

func Code

func Code(err error) string

Code returns the code of the root error, if available. Otherwise, returns INTERNAL.

func Is

func Is(err, target error) bool

Is calls the stdlib errors.Is.

func Message

func Message(err error) string

Message returns the human-readable message of the error, if available. Otherwise, returns a generic error message.

func New

func New(message string) error

New is a wrapper for the stdlib new function.

func Unwrap

func Unwrap(err error) error

Unwrap calls the stdlib errors.UnUnwrap.

Types

type Error

type Error struct {
	// The application error code.
	Code string `json:"code" bson:"code"`
	// A human-readable message to send back to the end user.
	Message string `json:"message" bson:"message"`
	// Defines what operation is currently being run.
	Operation string `json:"operation" bson:"op"`
	// The error that was returned from the caller.
	Err error `json:"error" bson:"error"`
	// contains filtered or unexported fields
}

Error defines a standard application error.

func ErrorF

func ErrorF(err error, op, format string, args ...any) *Error

ErrorF returns an Error with the DefaultCode and formatted message arguments.

func NewConflict

func NewConflict(err error, message, op string) *Error

NewConflict returns an Error with a CONFLICT error code.

func NewE

func NewE(err error, message, op string) *Error

NewE returns an Error with the DefaultCode.

func NewExpired

func NewExpired(err error, message, op string) *Error

NewExpired returns an Error with a EXPIRED error code.

func NewInternal

func NewInternal(err error, message, op string) *Error

NewInternal returns an Error with a INTERNAL error code.

func NewInvalid

func NewInvalid(err error, message, op string) *Error

NewInvalid returns an Error with a INVALID error code.

func NewMaximumAttempts

func NewMaximumAttempts(err error, message, op string) *Error

NewMaximumAttempts returns an Error with a MAXIMUMATTEMPTS error code.

func NewNotFound

func NewNotFound(err error, message, op string) *Error

NewNotFound returns an Error with a NOTFOUND error code.

func NewUnknown

func NewUnknown(err error, message, op string) *Error

NewUnknown returns an Error with a UNKNOWN error code.

func ToError

func ToError(err any) *Error

ToError Returns an application error from input. If The type is not of type Error, nil will be returned.

func Wrap

func Wrap(err error, message string) *Error

Wrap returns an error annotating err with a stack trace at the point Wrap is called, and the supplied message. If err is nil, Wrap returns nil.

func (*Error) Error

func (e *Error) Error() string

Error returns the string representation of the error message by implementing the error interface.

func (*Error) FileLine

func (e *Error) FileLine() string

FileLine returns the file and line in which the error occurred.

func (*Error) HTTPStatusCode

func (e *Error) HTTPStatusCode() int

HTTPStatusCode is a convenience method used to get the appropriate HTTP response status code for the respective error type.

func (*Error) MarshalJSON added in v0.0.2

func (e *Error) MarshalJSON() ([]byte, error)

MarshalJSON implements encoding/Marshaller to wrap the error as a string if there is one.

func (*Error) ProgramCounters

func (e *Error) ProgramCounters() []uintptr

ProgramCounters returns the slice of PC values associated with the error.

func (*Error) RuntimeFrames

func (e *Error) RuntimeFrames() *runtime.Frames

RuntimeFrames returns function/file/line information.

func (*Error) Scan added in v0.0.3

func (e *Error) Scan(value any) error

Scan implements the sql.Scanner interface.

func (*Error) StackTrace

func (e *Error) StackTrace() string

StackTrace returns a string representation of the errors stacktrace, where each trace is separated by a newline and tab '\t'.

func (*Error) StackTraceSlice

func (e *Error) StackTraceSlice() []string

StackTraceSlice returns a string slice of the errors stacktrace.

func (*Error) UnmarshalJSON added in v0.0.2

func (e *Error) UnmarshalJSON(data []byte) error

UnmarshalJSON implements encoding/Marshaller to unmarshal the wrapping error to type Error.

func (*Error) Unwrap

func (e *Error) Unwrap() error

Unwrap unwraps the original error message.

func (*Error) Value added in v0.0.3

func (e *Error) Value() (driver.Value, error)

Value implements the driver.Valuer interface.

Jump to

Keyboard shortcuts

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