# README
failure
Package failure
provides errors utilities for your application errors.
- Automatically generate awesome
err.Error
message for developers. - Flexible error messages for end users.
- Powerful and readable stack trace.
- Error context, such as function parameter, with key-value data.
- Extensible error chain.
Usage
At first, define error codes for your application.
const (
NotFound failure.StringCode = "NotFound"
InvalidArgument failure.StringCode = "InvalidArgument"
Internal failure.StringCode = "Internal"
)
Using failure.New
, return an error with error code.
return failure.New(NotFound)
Handle the error with failure.Is
and translate it into another error code with failure.Translate
.
if failure.Is(err, NotFound) {
return failure.Translate(err, Internal)
}
If you want to just return the error, use failure.Wrap
.
if err != nil {
return failure.Wrap(err)
}
An error context and message for end user can be attached.
func Foo(a, b string) error {
return failure.New(InvalidArgument,
failure.Context{"a": a, "b": b},
failure.Message("Given parameters are invalid!!"),
)
}
Awesome error message for developers.
func main() {
err := Bar()
fmt.Println(err)
fmt.Println("=====")
fmt.Printf("%+v\n", err)
}
func Bar() error {
err := Foo("hello", "world")
if err != nil {
return failure.Wrap(err)
}
return nil
}
main.Bar: main.Foo: a=hello b=world: Given parameters are invalid!!: code(InvalidArgument)
=====
[main.Bar] /tmp/sandbox615088634/prog.go:25
[main.Foo] /tmp/sandbox615088634/prog.go:31
a = hello
b = world
message("Given parameters are invalid!!")
code(InvalidArgument)
[CallStack]
[main.Foo] /tmp/sandbox615088634/prog.go:31
[main.Bar] /tmp/sandbox615088634/prog.go:23
[main.main] /tmp/sandbox615088634/prog.go:16
[runtime.main] /usr/local/go-faketime/src/runtime/proc.go:204
[runtime.goexit] /usr/local/go-faketime/src/runtime/asm_amd64.s:1374
package.FunctionName
like main.Bar
and main.Foo
is automatically added to error message.
With %+v
format, it prints the detailed error chain + the call stack of the first error.
Full Example for HTTP Server
Try it on The Go Playground!
Or click ▶
on the below to see the code.
package main
import (
"fmt"
"io"
"net/http"
"net/http/httptest"
"net/http/httputil"
"github.com/morikuni/failure"
)
// error codes for your application.
const (
NotFound failure.StringCode = "NotFound"
Forbidden failure.StringCode = "Forbidden"
)
func GetACL(projectID, userID string) (acl interface{}, e error) {
notFound := true
if notFound {
return nil, failure.New(NotFound,
failure.Context{"project_id": projectID, "user_id": userID},
)
}
return nil, failure.Unexpected("unexpected error")
}
func GetProject(projectID, userID string) (project interface{}, e error) {
_, err := GetACL(projectID, userID)
if err != nil {
if failure.Is(err, NotFound) {
return nil, failure.Translate(err, Forbidden,
failure.Message("no acl exists"),
failure.Context{"additional_info": "hello"},
)
}
return nil, failure.Wrap(err)
}
return nil, nil
}
func Handler(w http.ResponseWriter, r *http.Request) {
_, err := GetProject(r.FormValue("project_id"), r.FormValue("user_id"))
if err != nil {
HandleError(w, err)
return
}
}
func getHTTPStatus(err error) int {
c, ok := failure.CodeOf(err)
if !ok {
return http.StatusInternalServerError
}
switch c {
case NotFound:
return http.StatusNotFound
case Forbidden:
return http.StatusForbidden
default:
return http.StatusInternalServerError
}
}
func getMessage(err error) string {
msg, ok := failure.MessageOf(err)
if ok {
return msg
}
return "Error"
}
func HandleError(w http.ResponseWriter, err error) {
w.WriteHeader(getHTTPStatus(err))
io.WriteString(w, getMessage(err))
fmt.Println("============ Error ============")
fmt.Printf("Error = %v\n", err)
code, _ := failure.CodeOf(err)
fmt.Printf("Code = %v\n", code)
msg, _ := failure.MessageOf(err)
fmt.Printf("Message = %v\n", msg)
cs, _ := failure.CallStackOf(err)
fmt.Printf("CallStack = %v\n", cs)
fmt.Printf("Cause = %v\n", failure.CauseOf(err))
fmt.Println()
fmt.Println("============ Detail ============")
fmt.Printf("%+v\n", err)
// [main.GetProject] /go/src/github.com/morikuni/failure/example/main.go:36
// message("no acl exists")
// additional_info = hello
// code(Forbidden)
// [main.GetACL] /go/src/github.com/morikuni/failure/example/main.go:21
// project_id = 123
// user_id = 456
// code(NotFound)
// [CallStack]
// [main.GetACL] /go/src/github.com/morikuni/failure/example/main.go:21
// [main.GetProject] /go/src/github.com/morikuni/failure/example/main.go:33
// [main.Handler] /go/src/github.com/morikuni/failure/example/main.go:47
// [http.HandlerFunc.ServeHTTP] /usr/local/go/src/net/http/server.go:1964
// [http.(*ServeMux).ServeHTTP] /usr/local/go/src/net/http/server.go:2361
// [http.serverHandler.ServeHTTP] /usr/local/go/src/net/http/server.go:2741
// [http.(*conn).serve] /usr/local/go/src/net/http/server.go:1847
// [runtime.goexit] /usr/local/go/src/runtime/asm_amd64.s:1333
}
func main() {
req := httptest.NewRequest(http.MethodGet, "/?project_id=aaa&user_id=111", nil)
rec := httptest.NewRecorder()
Handler(rec, req)
res, _ := httputil.DumpResponse(rec.Result(), true)
fmt.Println("============ Dump ============")
fmt.Println(string(res))
}