https://github.com/morikuni/failure
failure is a utility package for handling application errors.
https://github.com/morikuni/failure
error error-handling go golang
Last synced: 3 months ago
JSON representation
failure is a utility package for handling application errors.
- Host: GitHub
- URL: https://github.com/morikuni/failure
- Owner: morikuni
- License: mit
- Created: 2018-06-18T06:46:34.000Z (almost 8 years ago)
- Default Branch: main
- Last Pushed: 2024-04-19T00:40:59.000Z (almost 2 years ago)
- Last Synced: 2024-11-17T12:11:10.363Z (over 1 year ago)
- Topics: error, error-handling, go, golang
- Language: Go
- Homepage:
- Size: 196 KB
- Stars: 363
- Watchers: 6
- Forks: 4
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- Funding: .github/FUNDING.yml
- License: LICENSE
Awesome Lists containing this project
README
# failure
[](https://pkg.go.dev/github.com/morikuni/failure/v2)
Package `failure` is an error handling library for Go. It allows you to create, wrap, and handle errors with additional context and features.
## Features
- Create errors with error codes to easily classify and handle errors.
- Wrap errors with additional context such as function parameters and key-value data.
- Automatically capture call stack information for debugging.
- Flexible error formatting for both developers and end users.
- Utility functions to extract error codes, messages, and other metadata from errors.
## Installation
To install `failure`, use the following command:
```
go get github.com/morikuni/failure/v2
```
## Usage Examples
First, define your application's error codes:
```go
type ErrorCode string
const (
ErrNotFound ErrorCode = "NotFound"
ErrInvalidArgument ErrorCode = "InvalidArgument"
)
```
Use `failure.New` to create a new error with an error code:
```go
err := failure.New(ErrNotFound, failure.Message("Resource not found"))
```
Use `failure.Wrap` to wrap an existing error with additional context:
```go
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:
```go
if failure.Is(err, ErrNotFound) {
// Handle ErrNotFound error
}
```
Use utility functions to extract metadata from the error:
```go
code := failure.CodeOf(err)
message := failure.MessageOf(err)
callStack := failure.CallStackOf(err)
```
Example error outputs:
```go
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](https://pkg.go.dev/github.com/morikuni/failure).
## Full Example of Usage
```go
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))
}
```
## Migration from v1 to v2
See [docs/v1-to-v2.md](docs/v1-to-v2.md) for migration guide.
## Contributing
Contributions are welcome! Feel free to send issues or pull requests.
## License
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.