1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
|
package grpcstatus
import (
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
type gRPCStatus interface {
GRPCStatus() *status.Status
}
func unwrapPkgErrorsGRPCStatus(err error) (*status.Status, bool) {
type causer interface {
Cause() error
}
// Unwrapping the github.com/pkg/errors causer interface, using `Cause` directly could miss some error implementing
// the `GRPCStatus` function so we have to check it on our selves.
unwrappedCauser := err
for unwrappedCauser != nil {
if s, ok := unwrappedCauser.(gRPCStatus); ok {
return s.GRPCStatus(), true
}
cause, ok := unwrappedCauser.(causer)
if !ok {
break
}
unwrappedCauser = cause.Cause()
}
return nil, false
}
// Since error can be wrapped and the `FromError` function only checks for `GRPCStatus` function
// and as a fallback uses the `Unknown` gRPC status we need to unwrap the error if possible to get the original status.
// pkg/errors and Go native errors packages have two different approaches so we try to unwrap both types.
// Eventually should be implemented in the go-grpc status function `FromError`. See https://github.com/grpc/grpc-go/issues/2934
func FromError(err error) (s *status.Status, ok bool) {
s, ok = status.FromError(err)
if ok {
return s, true
}
// Try to unwrap `github.com/pkg/errors` wrapped error
s, ok = unwrapPkgErrorsGRPCStatus(err)
if ok {
return s, true
}
// Try to unwrap native wrapped errors using `fmt.Errorf` and `%w`
s, ok = unwrapNativeWrappedGRPCStatus(err)
if ok {
return s, true
}
// We failed to unwrap any GRPSStatus so return default `Unknown`
return status.New(codes.Unknown, err.Error()), false
}
|