README
¶
Errors v1.0.0
Errors package is a drop-in replacement of the built-in Go errors package. 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.
Features of this package:
- Multiple (11) error types
- Easy handling of User friendly message(s)
- Stacktrace - formatted, unfromatted, custom format (refer tests in errors_test.go)
- Retrieve the Program Counters for the stacktrace
- Retrieve runtime.Frames using
errors.RuntimeFrames(err error)
for the stacktrace - HTTP status code and user friendly message (wrapped messages are concatenated) for all error types
- Helper functions to generate each error type
- Helper function to get error Type, error type as int, check if error type is wrapped anywhere in chain
fmt.Formatter
support
In case of nested errors, the messages & errors are also looped through the full chain of errors.
Prerequisites
Go 1.13+
Available error types
- TypeInternal - For internal system error. e.g. Database errors
- TypeValidation - For validation error. e.g. invalid email address
- TypeInputBody - For invalid input data. e.g. invalid JSON
- TypeDuplicate - For duplicate content error. e.g. user with email already exists (when trying to register a new user)
- TypeUnauthenticated - For not authenticated error
- TypeUnauthorized - For unauthorized access error
- TypeEmpty - For when an expected non-empty resource, is empty
- TypeNotFound - For expected resource not found. e.g. user ID not found
- TypeMaximumAttempts - For attempting the same action more than an allowed threshold
- TypeSubscriptionExpired - For when a user's 'paid' account has expired
- TypeDownstreamDependencyTimedout - 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 with the suffix 'Err'. All such helper functions accept the original error and a string.
package main
import (
"fmt"
"github.com/naughtygopher/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:", err)
fmt.Println("\nerr.Error():", err.Error())
fmt.Printf("\nformatted +v: %+v\n", err)
fmt.Printf("\nformatted v: %v\n", err)
fmt.Printf("\nformatted +s: %+s\n", err)
fmt.Printf("\nformatted s: %s\n", err)
_, msg, _ := errors.HTTPStatusCodeMessage(err)
fmt.Println("\nmsg:", msg)
}
Output
err: bar is not happy
err.Error(): /Users/k.balakumaran/go/src/github.com/naughtygopher/errors/cmd/main.go:16: bar is not happy
hello world!bar is not happy
formatted +v: /Users/k.balakumaran/go/src/github.com/naughtygopher/errors/cmd/main.go:16: bar is not happy
hello world!bar is not happy
formatted v: bar is not happy
formatted +s: bar is not happy: hello world!
formatted s: bar is not happy
msg: bar is not happy
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)
. However this errors package, whenever you call the Go error interface's Error() string
function, prints 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, it unwraps and returns a single concatenated message. Sample described in the 'How to use?' section
How to use?
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/naughtygopher/errors"
"github.com/naughtygopher/webgo/v6"
"github.com/naughtygopher/webgo/v6/middleware/accesslog"
)
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{
{
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(accesslog.AccessLog)
router.Use(accesslog.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 [2022-01-12]
MacBook Pro (13-inch, 2020, Four Thunderbolt 3 ports), 32 GB 3733 MHz LPDDR4X
$ go version
go version go1.19.5 darwin/amd64
$ go test -benchmem -bench .
goos: darwin
goarch: amd64
pkg: github.com/naughtygopher/errors
cpu: Intel(R) Core(TM) i7-1068NG7 CPU @ 2.30GHz
Benchmark_Internal-8 1526194 748.8 ns/op 1104 B/op 2 allocs/op
Benchmark_Internalf-8 1281465 944.0 ns/op 1128 B/op 3 allocs/op
Benchmark_InternalErr-8 1494351 806.7 ns/op 1104 B/op 2 allocs/op
Benchmark_InternalGetError-8 981162 1189 ns/op 1528 B/op 6 allocs/op
Benchmark_InternalGetErrorWithNestedError-8 896322 1267 ns/op 1544 B/op 6 allocs/op
Benchmark_InternalGetMessage-8 1492812 804.2 ns/op 1104 B/op 2 allocs/op
Benchmark_InternalGetMessageWithNestedError-8 1362092 886.3 ns/op 1128 B/op 3 allocs/op
Benchmark_HTTPStatusCodeMessage-8 27494096 41.38 ns/op 16 B/op 1 allocs/op
BenchmarkHasType-8 100000000 10.50 ns/op 0 B/op 0 allocs/op
PASS
ok github.com/naughtygopher/errors 15.006s
Contributing
More error types, customization, features, multi-errors; 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
- func As(err error, target interface{}) bool
- func ErrWithoutTrace(err error) (string, bool)
- func HTTPStatusCode(err error) (int, bool)
- func HTTPStatusCodeMessage(err error) (int, string, bool)
- func HasType(err error, et errType) bool
- func Is(err, target error) bool
- func Join(errs ...error) error
- func Message(err error) (string, bool)
- func ProgramCounters(err error) []uintptr
- func RuntimeFrames(err error) *runtime.Frames
- func SetDefaultType(e errType)
- func Stacktrace(err error) string
- func StacktraceCustomFormat(msgformat string, traceFormat string, err error) string
- func StacktraceFromPcs(err error) string
- func StacktraceNoFormat(err error) []string
- func Type(err error) errType
- func TypeInt(err error) int
- func Unwrap(err error) error
- func WriteHTTP(err error, w http.ResponseWriter)
- type Error
- func DownstreamDependencyTimedout(message string) *Error
- func DownstreamDependencyTimedoutErr(original error, message string) *Error
- func DownstreamDependencyTimedoutErrf(original error, format string, args ...interface{}) *Error
- func DownstreamDependencyTimedoutf(format string, args ...interface{}) *Error
- func Duplicate(message string) *Error
- func DuplicateErr(original error, message string) *Error
- func DuplicateErrf(original error, format string, args ...interface{}) *Error
- func Duplicatef(format string, args ...interface{}) *Error
- func Empty(message string) *Error
- func EmptyErr(original error, message string) *Error
- func EmptyErrf(original error, format string, args ...interface{}) *Error
- func Emptyf(format string, args ...interface{}) *Error
- func Errorf(fromat string, args ...interface{}) *Error
- func InputBody(message string) *Error
- func InputBodyErr(original error, message string) *Error
- func InputBodyErrf(original error, format string, args ...interface{}) *Error
- func InputBodyf(format string, args ...interface{}) *Error
- func Internal(message string) *Error
- func InternalErr(original error, message string) *Error
- func InternalErrf(original error, format string, args ...interface{}) *Error
- func Internalf(format string, args ...interface{}) *Error
- func MaximumAttempts(message string) *Error
- func MaximumAttemptsErr(original error, message string) *Error
- func MaximumAttemptsErrf(original error, format string, args ...interface{}) *Error
- func MaximumAttemptsf(format string, args ...interface{}) *Error
- func New(msg string) *Error
- func NewWithErrMsgType(original error, message string, etype errType) *Error
- func NewWithErrMsgTypef(original error, etype errType, format string, args ...interface{}) *Error
- func NewWithType(msg string, etype errType) *Error
- func NewWithTypef(etype errType, format string, args ...interface{}) *Error
- func Newf(fromat string, args ...interface{}) *Error
- func NotFound(message string) *Error
- func NotFoundErr(original error, message string) *Error
- func NotFoundErrf(original error, format string, args ...interface{}) *Error
- func NotFoundf(format string, args ...interface{}) *Error
- func SubscriptionExpired(message string) *Error
- func SubscriptionExpiredErr(original error, message string) *Error
- func SubscriptionExpiredErrf(original error, format string, args ...interface{}) *Error
- func SubscriptionExpiredf(format string, args ...interface{}) *Error
- func Unauthenticated(message string) *Error
- func UnauthenticatedErr(original error, message string) *Error
- func UnauthenticatedErrf(original error, format string, args ...interface{}) *Error
- func Unauthenticatedf(format string, args ...interface{}) *Error
- func Unauthorized(message string) *Error
- func UnauthorizedErr(original error, message string) *Error
- func UnauthorizedErrf(original error, format string, args ...interface{}) *Error
- func Unauthorizedf(format string, args ...interface{}) *Error
- func Validation(message string) *Error
- func ValidationErr(original error, message string) *Error
- func ValidationErrf(original error, format string, args ...interface{}) *Error
- func Validationf(format string, args ...interface{}) *Error
- func Wrap(original error, msg ...string) *Error
- func WrapWithMsg(original error, msg string) *Errordeprecated
- func Wrapf(original error, format string, args ...interface{}) *Error
- func (e *Error) Error() string
- func (e *Error) ErrorWithoutFileLine() string
- func (e *Error) Format(s fmt.State, verb rune)
- func (e *Error) HTTPStatusCode() int
- func (e *Error) Is(err error) bool
- func (e *Error) Message() string
- func (e *Error) ProgramCounters() []uintptr
- func (e *Error) RuntimeFrames() *runtime.Frames
- func (e *Error) StackTrace() []string
- func (e *Error) StackTraceCustomFormat(msgformat string, traceFormat string) []string
- func (e *Error) StackTraceNoFormat() []string
- func (e *Error) Type() errType
- func (e *Error) Unwrap() error
Constants ¶
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 // 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 ErrWithoutTrace ¶
ErrWithoutTrace is a duplicate of Message, but with clearer name. The boolean is 'true' if the provided err is of type *Error
func HTTPStatusCode ¶
HTTPStatusCode returns appropriate HTTP response status code based on type of the error. The boolean is 'true' if the provided error is of type *Err In case of joined errors, it'll return the status code of the last *Error
func HTTPStatusCodeMessage ¶
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 ¶
HasType will check if the provided err type is available anywhere nested in the error
func Message ¶
Message recursively concatenates all the messages set while creating/wrapping the errors. The boolean is 'true' if the provided error is of type *Err
func ProgramCounters ¶
func RuntimeFrames ¶
func SetDefaultType ¶
func SetDefaultType(e errType)
SetDefaultType will set the default error type, which is used in the 'New' function
func Stacktrace ¶
Stacktrace returns a string representation of the stacktrace, where each trace is separated by a newline and tab '\t'
func StacktraceCustomFormat ¶
StacktraceCustomFormat lets you prepare a stacktrace in a custom format
msgformat - is used to format the line which prints message from Error.message traceFormat - is used to format the line which prints trace Supported directives: %m - message if err type is *Error, otherwise output of `.Error()` %p - file path, empty if type is not *Error %l - line, empty if type is not *Error %f - function, empty if type is not *Error
func StacktraceFromPcs ¶
func StacktraceNoFormat ¶
Stacktrace returns a string representation of the stacktrace, as a slice of string where each element represents the error message and traces.
func Type ¶
func Type(err error) errType
Type returns the errType if it's an instance of *Error, -1 otherwise In case of joined error, it'll return the type of the last *Error
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 ¶
DownstreamDependencyTimedout is a helper function to create a new error of type TypeDownstreamDependencyTimedout
func DownstreamDependencyTimedoutErr ¶
DownstreamDependencyTimedoutErr is a helper function to create a new error of type TypeDownstreamDependencyTimedout which also accepts an original error
func DownstreamDependencyTimedoutErrf ¶
DownstreamDependencyTimedoutErrf is a helper function to create a new error of type TypeDownstreamDependencyTimedout which also accepts an original error, with formatted message
func DownstreamDependencyTimedoutf ¶
DownstreamDependencyTimedoutf is a helper function to create a new error of type TypeDownstreamDependencyTimedout, with formatted message
func DuplicateErr ¶
DuplicateErr is a helper function to create a new error of type TypeDuplicate which also accepts an original error
func DuplicateErrf ¶
DuplicateErrf is a helper function to create a new error of type TypeDuplicate which also accepts an original error, with formatted message
func Duplicatef ¶
Duplicatef is a helper function to create a new error of type TypeDuplicate, with formatted message
func EmptyErr ¶
EmptyErr is a helper function to create a new error of type TypeEmpty which also accepts an original error
func EmptyErrf ¶
EmptyErr is a helper function to create a new error of type TypeEmpty which also accepts an original error, with formatted message
func Emptyf ¶
Emptyf is a helper function to create a new error of type TypeEmpty, with formatted message
func Errorf ¶
Errorf is a convenience method to create a new instance of Error with formatted message Important: %w directive is not supported, use fmt.Errorf if you're using the %w directive or use Wrap/Wrapf to wrap an error.
func InputBodyErr ¶
InputBodyErr is a helper function to create a new error of type TypeInputBody which also accepts an original error
func InputBodyErrf ¶
InputBodyErrf is a helper function to create a new error of type TypeInputBody which also accepts an original error, with formatted message
func InputBodyf ¶
InputBodyf is a helper function to create a new error of type TypeInputBody, with formatted message
func InternalErr ¶
InternalErr helper method for creation internal errors which also accepts an original error
func InternalErrf ¶
InternalErr helper method for creation internal errors which also accepts an original error, with formatted message
func MaximumAttempts ¶
MaximumAttempts is a helper function to create a new error of type TypeMaximumAttempts
func MaximumAttemptsErr ¶
MaximumAttemptsErr is a helper function to create a new error of type TypeMaximumAttempts which also accepts an original error
func MaximumAttemptsErrf ¶
MaximumAttemptsErr is a helper function to create a new error of type TypeMaximumAttempts which also accepts an original error, with formatted message
func MaximumAttemptsf ¶
MaximumAttemptsf is a helper function to create a new error of type TypeMaximumAttempts, with formatted message
func NewWithErrMsgType ¶
NewWithErrMsgType returns an error instance with custom error type and message
func NewWithErrMsgTypef ¶
NewWithErrMsgTypef returns an error instance with custom error type and formatted message
func NewWithType ¶
NewWithType returns an error instance with custom error type
func NewWithTypef ¶
NewWithTypef returns an error instance with custom error type. And formatted message
func NotFoundErr ¶
NotFoundErr is a helper function to create a new error of type TypeNotFound which also accepts an original error
func NotFoundErrf ¶
NotFoundErrf is a helper function to create a new error of type TypeNotFound which also accepts an original error, with formatted message
func NotFoundf ¶
NotFoundf is a helper function to create a new error of type TypeNotFound, with formatted message
func SubscriptionExpired ¶
SubscriptionExpired is a helper function to create a new error of type TypeSubscriptionExpired
func SubscriptionExpiredErr ¶
SubscriptionExpiredErr is a helper function to create a new error of type TypeSubscriptionExpired which also accepts an original error
func SubscriptionExpiredErrf ¶
SubscriptionExpiredErrf is a helper function to create a new error of type TypeSubscriptionExpired which also accepts an original error, with formatted message
func SubscriptionExpiredf ¶
SubscriptionExpiredf is a helper function to create a new error of type TypeSubscriptionExpired, with formatted message
func Unauthenticated ¶
Unauthenticated is a helper function to create a new error of type TypeUnauthenticated
func UnauthenticatedErr ¶
UnauthenticatedErr is a helper function to create a new error of type TypeUnauthenticated which also accepts an original error
func UnauthenticatedErrf ¶
UnauthenticatedErrf is a helper function to create a new error of type TypeUnauthenticated which also accepts an original error, with formatted message
func Unauthenticatedf ¶
Unauthenticatedf is a helper function to create a new error of type TypeUnauthenticated, with formatted message
func Unauthorized ¶
Unauthorized is a helper function to create a new error of type TypeUnauthorized
func UnauthorizedErr ¶
UnauthorizedErr is a helper function to create a new error of type TypeUnauthorized which also accepts an original error
func UnauthorizedErrf ¶
UnauthorizedErrf is a helper function to create a new error of type TypeUnauthorized which also accepts an original error, with formatted message
func Unauthorizedf ¶
Unauthorizedf is a helper function to create a new error of type TypeUnauthorized, with formatted message
func Validation ¶
Validation is a helper function to create a new error of type TypeValidation
func ValidationErr ¶
ValidationErr helper method for creation validation errors which also accepts an original error
func ValidationErrf ¶
ValidationErr helper method for creation validation errors which also accepts an original error, with formatted message
func Validationf ¶
Validationf is a helper function to create a new error of type TypeValidation, with formatted message
func Wrap ¶
Wrap is used to simply wrap an error with optional message; error type would be the default error type set using SetDefaultType; TypeInternal otherwise If the error being wrapped is already of type Error, then its respective type is used
func WrapWithMsg
deprecated
func (*Error) ErrorWithoutFileLine ¶
ErrorWithoutFileLine prints the final string without the stack trace / file+line number
func (*Error) Format ¶
Format implements the verbs/directives supported by Error to be used in fmt annotated/formatted strings
%v - the same output as Message(). i.e. recursively get all the custom messages set by user
- if any of the wrapped error is not of type *Error, that will *not* be displayed
%+v - recursively prints all the messages along with the file & line number. Also includes output of `Error()` of non *Error types.
%s - identical to %v %+s - recursively prints all the messages without file & line number. Also includes output `Error()` of non *Error types.
func (*Error) HTTPStatusCode ¶
HTTPStatusCode is a convenience method used to get the appropriate HTTP response status code for the respective error type
func (*Error) Message ¶
Message returns the user friendly message stored in the error struct. It will ignore all errors which are not of type *Error
func (*Error) ProgramCounters ¶
func (*Error) RuntimeFrames ¶
func (*Error) StackTrace ¶
func (*Error) StackTraceCustomFormat ¶
StackTraceCustomFormat lets you prepare a stacktrace in a custom format
Supported directives: %m - message %p - file path %l - line %f - function