failure is a utility package for handling application errors.
Package failure
is an error handling library for Go. It allows you to create, wrap, and handle errors with additional context and features.
To install failure
, use the following command:
go get github.com/morikuni/failure/v2
First, define your application's error codes:
type ErrorCode string
const (
ErrNotFound ErrorCode = "NotFound"
ErrInvalidArgument ErrorCode = "InvalidArgument"
)
Use failure.New
to create a new error with an error code:
err := failure.New(ErrNotFound, failure.Message("Resource not found"))
Use failure.Wrap
to wrap an existing error with additional context:
if err != nil {
return failure.Wrap(err, failure.Context{"parameter": "value"})
}
Use failure.Is
to check for a specific error code and handle the error:
if failure.Is(err, ErrNotFound) {
// Handle ErrNotFound error
}
Use utility functions to extract metadata from the error:
code := failure.CodeOf(err)
message := failure.MessageOf(err)
callStack := failure.CallStackOf(err)
Example error outputs:
err := failure.New(ErrInvalidArgument, failure.Message("Invalid argument"), failure.Context{"userId": "123"})
fmt.Println(err)
// Output: GetUser[InvalidArgument](Invalid argument, {userId=123})
fmt.Printf("%+v\n", err)
// Output:
// [main.GetUser] /path/to/file.go:123
// InvalidArgument
// Invalid argument
// {userId=123}
// [CallStack]
// [main.GetUser] /path/to/file.go:123
// [main.main] /path/to/main.go:456
For more detailed usage and examples, refer to the Go Reference.
package main
import (
"fmt"
"io"
"net/http"
"net/http/httptest"
"net/http/httputil"
"github.com/morikuni/failure/v2"
)
type ErrorCode string
const (
NotFound ErrorCode = "NotFound"
Forbidden ErrorCode = "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 {
switch failure.CodeOf(err) {
case NotFound:
return http.StatusNotFound
case Forbidden:
return http.StatusForbidden
default:
return http.StatusInternalServerError
}
}
func getMessage(err error) string {
msg := failure.MessageOf(err)
if msg != "" {
return string(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)
// Error = main.GetProject[Forbidden](no acl exists, {additional_info=hello}): main.GetACL[NotFound]({project_id=aaa,user_id=111})
code := failure.CodeOf(err)
fmt.Printf("Code = %v\n", code)
// Code = Forbidden
msg := failure.MessageOf(err)
fmt.Printf("Message = %v\n", msg)
// Message = no acl exists
cs := failure.CallStackOf(err)
fmt.Printf("CallStack = %v\n", cs)
// CallStack = main.GetACL: main.GetProject: main.Handler: main.main: runtime.main: goexit
fmt.Printf("Cause = %v\n", failure.CauseOf(err))
// Cause = main.GetACL[NotFound]({project_id=aaa,user_id=111})
fmt.Println()
fmt.Println("============ Detail ============")
fmt.Printf("%+v\n", err)
// [main.GetProject] /go/src/github.com/morikuni/failure/example/main.go:34
// Forbidden
// no acl exists
// {additional_info=hello}
// [main.GetACL] /go/src/github.com/morikuni/failure/example/main.go:23
// NotFound
// {user_id=111,project_id=aaa}
// [CallStack]
// [main.GetACL] /go/src/github.com/morikuni/failure/example/main.go:23
// [main.GetProject] /go/src/github.com/morikuni/failure/example/main.go:31
// [main.Handler] /go/src/github.com/morikuni/failure/example/main.go:45
// [main.main] /go/src/github.com/morikuni/failure/example/main.go:119
// [runtime.main] /opt/homebrew/opt/go/libexec/src/runtime/proc.go:271
// [runtime.goexit] /opt/homebrew/opt/go/libexec/src/runtime/asm_arm64.s:1222
}
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))
}
See docs/v1-to-v2.md for migration guide.
Contributions are welcome! Feel free to send issues or pull requests.
This project is licensed under the MIT License. See the LICENSE file for details.