library-go/errors/readme.md

125 lines
3.2 KiB
Markdown
Raw Normal View History

## Purpose of errs package
1. compatible with `error` interface
2. encapsulate error message with functions in `easy_function.go`
3. easy for gRPC client/server
4. support err's chain by [Working with Errors in Go 1.13](https://blog.golang.org/go1.13-errors)
## Example - Normal function
Using builtin functions `InvalidInput` to generate `Err struct`
**please add your own functions if not exist**
```go
package main
import "adc.github.trendmicro.com/commercial-mgcp/library-go/pkg/errs"
func handleParam(s string) error {
// check user_id format
if ok := userIDFormat(s); !ok {
return errs.InvalidFormat("param user_id")
}
return nil
}
```
## Example - gRPC Server
`GetAgent` is a method of gRPC server, it wraps `Err` struct to `status.Status` struct
```go
func (as *agentService) GetAgent(ctx context.Context, req *cloudep.GetAgentRequest) (*cloudep.GetAgentResponse, error) {
l := log.WithFields(logger.Fields{"tenant_id": req.TenantId, "agent_id": req.AgentId, "method": "GetAgent"})
tenantID, err := primitive.ObjectIDFromHex(req.TenantId)
if err != nil {
// err maybe errs.Err or general error
// it's safe to use Convert() here
return nil, status.Convert(err).Err()
}
...
}
```
## Example - gRPC Client
Calling `GetAgent` and retry when Category is "DB"
```go
client := cloudep.NewAgentServiceClient(conn)
req := cloudep.GetAgentRequest{
TenantId: "not-a-valid-object-id",
AgentId: "5eb4fa99006d53c0cb6f9cfe",
}
// Retry if DB error
for retry := 3; retry > 0 ; retry-- {
resp, err := client.GetAgent(context.Background(), &req)
if err != nil {
e := errs.FromGRPCError(err)
if e.Category() == code.CatGRPC {
if e.Code() == uint32(codes.Unavailable) {
log.warn("GRPC service unavailable. Retrying...")
continue
}
log.errorf("GRPC built-in error: %v", e)
}
if e.Category() == code.CatDB {
log.warn("retry...")
continue
}
}
break
}
```
## Example - REST server
1. handling gRPC client error
2. transfer to HTTP code
3. transfer to Error body
```go
func Handler(c *gin.Context) {
// handle error from gRPC client
resp, err := client.GetAgent(context.Background(), &req)
if err != nil {
// to Err
e := errs.FromGRPCError(err)
// get HTTP code & response struct
// 2nd parameter true means return general error message to user
c.JSON(e.HTTPStatus(), general.NewError(e, true))
}
}
```
## Example - Error Chain
1. set internal error by func `Wrap`
2. check Err has any error in err's chain matches the target by `errors.Is`
3. finds the first error in err's chain that matches target by `errors.As`
```go
// define a specific err type
type testErr struct {
code int
}
func (e *testErr) Error() string {
return strconv.Itoa(e.code)
}
func main() {
layer1Err := &testErr{code: 123}
// error chain: InvalidFormat -> layer 1 err
layer2Err := InvalidFormat("field A", "")
layer2Err.Wrap(layer1Err) //set internal error
// errors.Is should report true
hasLayer1Err := errors.Is(layer2Err, layer1Err)
// errors.As should return internal error
var internalErr *testErr
ok := errors.As(layer2Err, &internalErr)
}
```