errors

package module
v0.3.5 Latest Latest
Warning

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

Go to latest
Published: Feb 13, 2021 License: MIT Imports: 6 Imported by: 45

README

errors gopher

Build Status codecov Go Report Card Maintainability

Errors

Errors package is a drop-in replacement of the built-in Go errors package with no external dependencies. It lets you create errors of 11 different types which should handle most of the use cases. Some of them are a bit too specific for web applications, but useful nonetheless. Following are the primary features of this package:

  1. Multiple (11) error types
  2. User friendly message
  3. File & line number prefixed to errors
  4. HTTP status code and user friendly message (wrapped messages are concatenated) for all error types
  5. Helper functions to generate each error type
  6. Helper function to get error Type, error type as int, check if error type is wrapped anywhere in chain

In case of nested errors, the messages & errors are also looped through the full chain of errors.

Prerequisites

Go 1.13+

Available error types
  1. TypeInternal - is the error type for when there is an internal system error. e.g. Database errors
  2. TypeValidation - is the error type for when there is a validation error. e.g. invalid email address
  3. TypeInputBody - is the error type for when the input data is invalid. e.g. invalid JSON
  4. TypeDuplicate - is the error type for when there's duplicate content. e.g. user with email already exists (when trying to register a new user)
  5. TypeUnauthenticated - is the error type when trying to access an authenticated API without authentication
  6. TypeUnauthorized - is the error type for when there's an unauthorized access attempt
  7. TypeEmpty - is the error type for when an expected non-empty resource, is empty
  8. TypeNotFound - is the error type for an expected resource is not found. e.g. user ID not found
  9. TypeMaximumAttempts - is the error type for attempting the same action more than an allowed threshold
  10. TypeSubscriptionExpired - is the error type for when a user's 'paid' account has expired
  11. TypeDownstreamDependencyTimedout - is the error type for when a request to a downstream dependent service times out

Helper functions are available for all the error types. Each of them have 2 helper functions, one which accepts only a string, and the other which accepts an original error as well as a user friendly message.

All the dedicated error type functions are documented here. Names are consistent with the error type, e.g. errors.Internal(string) and errors.InternalErr(error, string)

User friendly messages

More often than not, when writing APIs, we'd want to respond with an easier to undersand user friendly message. Instead of returning the raw error. And log the raw error.

There are helper functions for all the error types, when in need of setting a friendly message, there are helper functions have a suffix 'Err'. All such helper functions accept the original error and a string.

package main
import(
    "fmt"
    "github.com/bnkamalesh/errors"
)

func Bar() error {
    return fmt.Errorf("hello %s", "world!")
}

func Foo() error {
    err := Bar()
    if err != nil {
        return errors.InternalErr(err, "bar is not happy")
    }
    return nil
}

func main() {
    err := Foo()
    fmt.Println(err)
    _,msg,_ := errors.HTTPStatusCodeMessage(err)
    fmt.Println(msg)
}
File & line number prefixed to errors

A common annoyance with Go errors which most people are aware of is, figuring out the origin of the error, especially when there are nested function calls. Ever since error annotation was introduced in Go, a lot of people have tried using it to trace out an errors origin by giving function names, contextual message etc in it. e.g. fmt.Errorf("database query returned error %w", err). This errors package, whenever you call the Go error interface's Error() string function, it'll print the error prefixed by the filepath and line number. It'd look like ../Users/JohnDoe/apps/main.go:50 hello world where 'hello world' is the error message.

HTTP status code & message

The function errors.HTTPStatusCodeMessage(error) (int, string, bool) returns the HTTP status code, message, and a boolean value. The boolean is true, if the error is of type *Error from this package. If error is nested with multiple errors, it loops through all the levels and returns a single concatenated message. This is illustrated in the 'How to use?' section

How to use?

Before that, over the years I have tried error with stack trace, annotation, custom error package with error codes etc. Finally, I think this package gives the best of all worlds, for most generic usecases.

A sample was already shown in the user friendly message section, following one would show a few more scenarios.

package main

import (
	"fmt"
	"net/http"
	"time"

	"github.com/bnkamalesh/errors"
	"github.com/bnkamalesh/webgo/v4"
	"github.com/bnkamalesh/webgo/v4/middleware"
)

func bar() error {
	return fmt.Errorf("%s %s", "sinking", "bar")
}

func bar2() error {
	err := bar()
	if err != nil {
		return errors.InternalErr(err, "bar2 was deceived by bar1 :(")
	}
	return nil
}

func foo() error {
	err := bar2()
	if err != nil {
		return errors.InternalErr(err, "we lost bar2!")
	}
	return nil
}

func handler(w http.ResponseWriter, r *http.Request) {
	err := foo()
	if err != nil {
		// log the error on your server for troubleshooting
		fmt.Println(err.Error())
		// respond to request with friendly msg
		status, msg, _ := errors.HTTPStatusCodeMessage(err)
		webgo.SendError(w, msg, status)
		return
	}

	webgo.R200(w, "yay!")
}

func routes() []*webgo.Route {
	return []*webgo.Route{
		&webgo.Route{
			Name:    "home",
			Method:  http.MethodGet,
			Pattern: "/",
			Handlers: []http.HandlerFunc{
				handler,
			},
		},
	}
}

func main() {
	router := webgo.NewRouter(&webgo.Config{
		Host:         "",
		Port:         "8080",
		ReadTimeout:  15 * time.Second,
		WriteTimeout: 60 * time.Second,
	}, routes())

	router.UseOnSpecialHandlers(middleware.AccessLog)
	router.Use(middleware.AccessLog)
	router.Start()
}

webgo was used to illustrate the usage of the function, errors.HTTPStatusCodeMessage. It returns the appropriate http status code, user friendly message stored within, and a boolean value. Boolean value is true if the returned error of type *Error. Since we get the status code and message separately, when using any web framework, you can set values according to the respective framework's native functions. In case of Webgo, it wraps errors in a struct of its own. Otherwise, you could directly respond to the HTTP request by calling errors.WriteHTTP(error,http.ResponseWriter).

Once the app is running, you can check the response by opening http://localhost:8080 on your browser. Or on terminal

$ curl http://localhost:8080
{"errors":"we lost bar2!. bar2 was deceived by bar1 :(","status":500} // output

And the fmt.Println(err.Error()) generated output on stdout would be:

/Users/username/go/src/errorscheck/main.go:28 /Users/username/go/src/errorscheck/main.go:20 sinking bar

Benchmark

Benchmark run on:

Screenshot 2020-07-18 at 6 25 22 PM

Results

$ go version
go version go1.14.4 darwin/amd64
$ go test -bench=.
goos: darwin
goarch: amd64
pkg: github.com/bnkamalesh/errors
Benchmark_Internal-8                            	 1874256	       639 ns/op	     368 B/op	       5 allocs/op
Benchmark_InternalErr-8                         	 1612707	       755 ns/op	     368 B/op	       5 allocs/op
Benchmark_InternalGetError-8                    	 1700966	       706 ns/op	     464 B/op	       6 allocs/op
Benchmark_InternalGetErrorWithNestedError-8     	 1458368	       823 ns/op	     464 B/op	       6 allocs/op
Benchmark_InternalGetMessage-8                  	 1866562	       643 ns/op	     368 B/op	       5 allocs/op
Benchmark_InternalGetMessageWithNestedError-8   	 1656597	       770 ns/op	     400 B/op	       6 allocs/op
Benchmark_HTTPStatusCodeMessage-8               	26003678	        46.1 ns/op	      16 B/op	       1 allocs/op
BenchmarkHasType-8                              	84689433	        14.2 ns/op	       0 B/op	       0 allocs/op
PASS
ok  	github.com/bnkamalesh/errors	14.478s

Contributing

More error types, customization etc; PRs & issues are welcome!

The gopher

The gopher used here was created using Gopherize.me. Show some love to Go errors like our gopher lady here!

Documentation

Overview

Package errors helps in wrapping errors with custom type as well as a user friendly message. This is particularly useful when responding to APIs

Index

Constants

View Source
const (
	// TypeInternal is error type for when there is an internal system error. e.g. Database errors
	TypeInternal errType = iota
	// TypeValidation is error type for when there is a validation error. e.g. invalid email address
	TypeValidation
	// TypeInputBody is error type for when an input data type error. e.g. invalid JSON
	TypeInputBody
	// TypeDuplicate is error type for when there's duplicate content
	TypeDuplicate
	// TypeUnauthenticated is error type when trying to access an authenticated API without authentication
	TypeUnauthenticated
	// TypeUnauthorized is error type for when there's an unauthorized access attempt
	TypeUnauthorized
	// TypeEmpty is error type for when an expected non-empty resource, is empty
	TypeEmpty
	// TypeNotFound is error type for an expected resource is not found e.g. user ID not found
	TypeNotFound
	// TypeMaximumAttempts is error type for attempting the same action more than allowed
	TypeMaximumAttempts
	// TypeSubscriptionExpired is error type for when a user's 'paid' account has expired
	TypeSubscriptionExpired
	// TypeDownstreamDependencyTimedout is error type for when a request to a downstream dependent service times out
	TypeDownstreamDependencyTimedout

	// DefaultMessage is the default user friendly message
	DefaultMessage = "unknown error occurred"
)

While adding a new Type, the respective helper functions should be added, also update the WriteHTTP method accordingly

Variables

This section is empty.

Functions

func As

func As(err error, target interface{}) bool

As calls the Go builtin errors.As

func HTTPStatusCodeMessage

func HTTPStatusCodeMessage(err error) (int, string, bool)

HTTPStatusCodeMessage returns the appropriate HTTP status code, message, boolean for the error the boolean value is true if the error was of type *Error, false otherwise

func HasType added in v0.1.10

func HasType(err error, et errType) bool

HasType will check if the provided err type is available anywhere nested in the error

func Is

func Is(err, target error) bool

Is calls the Go builtin errors.Is

func SetDefaultType

func SetDefaultType(e errType)

SetDefaultType will set the default error type, which is used in the 'New' function

func Type added in v0.1.10

func Type(err error) errType

Type returns the errType if it's an instance of *Error, -1 otherwise

func TypeInt added in v0.1.10

func TypeInt(err error) int

Type returns the errType as integer if it's an instance of *Error, -1 otherwise

func Unwrap

func Unwrap(err error) error

Unwrap calls the Go builtin errors.UnUnwrap

func WriteHTTP

func WriteHTTP(err error, w http.ResponseWriter)

WriteHTTP is a convenience method which will check if the error is of type *Error and respond appropriately

Types

type Error

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

Error is the struct which holds custom attributes

func DownstreamDependencyTimedout

func DownstreamDependencyTimedout(message string) *Error

DownstreamDependencyTimedout is a helper function to create a new error of type TypeDownstreamDependencyTimedout

func DownstreamDependencyTimedoutErr

func DownstreamDependencyTimedoutErr(original error, message string) *Error

DownstreamDependencyTimedoutErr is a helper function to create a new error of type TypeDownstreamDependencyTimedout which also accepts an original error

func DownstreamDependencyTimedoutErrf added in v0.3.0

func DownstreamDependencyTimedoutErrf(original error, format string, args ...interface{}) *Error

DownstreamDependencyTimedoutErrf is a helper function to create a new error of type TypeDownstreamDependencyTimedout which also accepts an original error, with formatted message

func DownstreamDependencyTimedoutf added in v0.3.0

func DownstreamDependencyTimedoutf(format string, args ...interface{}) *Error

DownstreamDependencyTimedoutf is a helper function to create a new error of type TypeDownstreamDependencyTimedout, with formatted message

func Duplicate

func Duplicate(message string) *Error

Duplicate is a helper function to create a new error of type TypeDuplicate

func DuplicateErr

func DuplicateErr(original error, message string) *Error

DuplicateErr is a helper function to create a new error of type TypeDuplicate which also accepts an original error

func DuplicateErrf added in v0.3.0

func DuplicateErrf(original error, format string, args ...interface{}) *Error

DuplicateErrf is a helper function to create a new error of type TypeDuplicate which also accepts an original error, with formatted message

func Duplicatef added in v0.3.0

func Duplicatef(format string, args ...interface{}) *Error

Duplicatef is a helper function to create a new error of type TypeDuplicate, with formatted message

func Empty

func Empty(message string) *Error

Empty is a helper function to create a new error of type TypeEmpty

func EmptyErr

func EmptyErr(original error, message string) *Error

EmptyErr is a helper function to create a new error of type TypeEmpty which also accepts an original error

func EmptyErrf added in v0.3.0

func EmptyErrf(original error, format string, args ...interface{}) *Error

EmptyErr is a helper function to create a new error of type TypeEmpty which also accepts an original error, with formatted message

func Emptyf added in v0.3.0

func Emptyf(format string, args ...interface{}) *Error

Emptyf is a helper function to create a new error of type TypeEmpty, with formatted message

func Errorf added in v0.3.4

func Errorf(fromat string, args ...interface{}) *Error

func InputBody

func InputBody(message string) *Error

InputBody is a helper function to create a new error of type TypeInputBody

func InputBodyErr

func InputBodyErr(original error, message string) *Error

InputBodyErr is a helper function to create a new error of type TypeInputBody which also accepts an original error

func InputBodyErrf added in v0.3.0

func InputBodyErrf(original error, format string, args ...interface{}) *Error

InputBodyErrf is a helper function to create a new error of type TypeInputBody which also accepts an original error, with formatted message

func InputBodyf added in v0.3.0

func InputBodyf(format string, args ...interface{}) *Error

InputBodyf is a helper function to create a new error of type TypeInputBody, with formatted message

func Internal added in v0.1.7

func Internal(message string) *Error

Internal helper method for creating internal errors

func InternalErr

func InternalErr(original error, message string) *Error

InternalErr helper method for creation internal errors which also accepts an original error

func InternalErrf added in v0.3.0

func InternalErrf(original error, format string, args ...interface{}) *Error

InternalErr helper method for creation internal errors which also accepts an original error, with formatted message

func Internalf added in v0.3.0

func Internalf(format string, args ...interface{}) *Error

Internalf helper method for creating internal errors with formatted message

func MaximumAttempts

func MaximumAttempts(message string) *Error

MaximumAttempts is a helper function to create a new error of type TypeMaximumAttempts

func MaximumAttemptsErr

func MaximumAttemptsErr(original error, message string) *Error

MaximumAttemptsErr is a helper function to create a new error of type TypeMaximumAttempts which also accepts an original error

func MaximumAttemptsErrf added in v0.3.0

func MaximumAttemptsErrf(original error, format string, args ...interface{}) *Error

MaximumAttemptsErr is a helper function to create a new error of type TypeMaximumAttempts which also accepts an original error, with formatted message

func MaximumAttemptsf added in v0.3.0

func MaximumAttemptsf(format string, args ...interface{}) *Error

MaximumAttemptsf is a helper function to create a new error of type TypeMaximumAttempts, with formatted message

func New

func New(msg string) *Error

New returns a new instance of Error with the relavant fields initialized

func NewWithErrMsgType

func NewWithErrMsgType(original error, message string, etype errType) *Error

NewWithErrMsgType returns an error instance with custom error type and message

func NewWithErrMsgTypef added in v0.3.0

func NewWithErrMsgTypef(original error, etype errType, format string, args ...interface{}) *Error

NewWithErrMsgTypef returns an error instance with custom error type and formatted message

func NewWithType

func NewWithType(msg string, etype errType) *Error

NewWithType returns an error instance with custom error type

func NewWithTypef added in v0.3.0

func NewWithTypef(etype errType, format string, args ...interface{}) *Error

NewWithTypef returns an error instance with custom error type. And formatted message

func Newf added in v0.3.4

func Newf(fromat string, args ...interface{}) *Error

func NotFound

func NotFound(message string) *Error

NotFound is a helper function to create a new error of type TypeNotFound

func NotFoundErr

func NotFoundErr(original error, message string) *Error

NotFoundErr is a helper function to create a new error of type TypeNotFound which also accepts an original error

func NotFoundErrf added in v0.3.0

func NotFoundErrf(original error, format string, args ...interface{}) *Error

NotFoundErrf is a helper function to create a new error of type TypeNotFound which also accepts an original error, with formatted message

func NotFoundf added in v0.3.0

func NotFoundf(format string, args ...interface{}) *Error

NotFoundf is a helper function to create a new error of type TypeNotFound, with formatted message

func SubscriptionExpired

func SubscriptionExpired(message string) *Error

SubscriptionExpired is a helper function to create a new error of type TypeSubscriptionExpired

func SubscriptionExpiredErr

func SubscriptionExpiredErr(original error, message string) *Error

SubscriptionExpiredErr is a helper function to create a new error of type TypeSubscriptionExpired which also accepts an original error

func SubscriptionExpiredErrf added in v0.3.0

func SubscriptionExpiredErrf(original error, format string, args ...interface{}) *Error

SubscriptionExpiredErrf is a helper function to create a new error of type TypeSubscriptionExpired which also accepts an original error, with formatted message

func SubscriptionExpiredf added in v0.3.0

func SubscriptionExpiredf(format string, args ...interface{}) *Error

SubscriptionExpiredf is a helper function to create a new error of type TypeSubscriptionExpired, with formatted message

func Unauthenticated

func Unauthenticated(message string) *Error

Unauthenticated is a helper function to create a new error of type TypeUnauthenticated

func UnauthenticatedErr

func UnauthenticatedErr(original error, message string) *Error

UnauthenticatedErr is a helper function to create a new error of type TypeUnauthenticated which also accepts an original error

func UnauthenticatedErrf added in v0.3.0

func UnauthenticatedErrf(original error, format string, args ...interface{}) *Error

UnauthenticatedErrf is a helper function to create a new error of type TypeUnauthenticated which also accepts an original error, with formatted message

func Unauthenticatedf added in v0.3.0

func Unauthenticatedf(format string, args ...interface{}) *Error

Unauthenticatedf is a helper function to create a new error of type TypeUnauthenticated, with formatted message

func Unauthorized

func Unauthorized(message string) *Error

Unauthorized is a helper function to create a new error of type TypeUnauthorized

func UnauthorizedErr

func UnauthorizedErr(original error, message string) *Error

UnauthorizedErr is a helper function to create a new error of type TypeUnauthorized which also accepts an original error

func UnauthorizedErrf added in v0.3.0

func UnauthorizedErrf(original error, format string, args ...interface{}) *Error

UnauthorizedErrf is a helper function to create a new error of type TypeUnauthorized which also accepts an original error, with formatted message

func Unauthorizedf added in v0.3.0

func Unauthorizedf(format string, args ...interface{}) *Error

Unauthorizedf is a helper function to create a new error of type TypeUnauthorized, with formatted message

func Validation

func Validation(message string) *Error

Validation is a helper function to create a new error of type TypeValidation

func ValidationErr

func ValidationErr(original error, message string) *Error

ValidationErr helper method for creation validation errors which also accepts an original error

func ValidationErrf added in v0.3.0

func ValidationErrf(original error, format string, args ...interface{}) *Error

ValidationErr helper method for creation validation errors which also accepts an original error, with formatted message

func Validationf added in v0.3.0

func Validationf(format string, args ...interface{}) *Error

Validationf is a helper function to create a new error of type TypeValidation, with formatted message

func Wrap added in v0.1.12

func Wrap(original error, msg ...string) *Error

Wrap is used to simply wrap an error with no custom error message with Error struct; with the error type being defaulted to `TypeInternal` If the error being wrapped is already of type Error, then its respective type is used

func WrapWithMsg added in v0.1.13

func WrapWithMsg(original error, msg string) *Error

WrapWithMsg [deprecated, use `Wrap`] wrap error with a user friendly message

func Wrapf added in v0.3.0

func Wrapf(original error, format string, args ...interface{}) *Error

func (*Error) Error

func (e *Error) Error() string

Error is the implementation of error interface

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) Is added in v0.1.6

func (e *Error) Is(err error) bool

Is implements the Is interface required by Go

func (*Error) Message

func (e *Error) Message() string

Message returns the user friendly message stored in the error struct. It will ignore all errors which are not of type *Error

func (*Error) Type

func (e *Error) Type() errType

Type returns the error type as integer

func (*Error) Unwrap added in v0.1.6

func (e *Error) Unwrap() error

Unwrap implement's Go 1.13's Unwrap interface exposing the wrapped error

Jump to

Keyboard shortcuts

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