[Golang] gRPC のエラーハンドリング
gRPC のエラーは、Status
として定義されている。
Go では status
と codes
という 2 つのパッケージを使って、gRPC のエラー Status
をさばく。
たとえば、とあるサービスが他のサービス client
を叩くときは、こんな感じになる。
import (
"fmt"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
func main() {
res, err := client.Get(id)
if err == nil {
return res, nil
}
log.Println(convertErr(err))
}
func convertErr(err error) error {
if err == nil {
return nil
}
// 返却されたエラーをパース
s, ok := status.FromError(err)
if !ok {
return fmt.Sprintf("Unknown Error: %+v\n", err)
}
// s.Code() で gRPC のエラーコードを取得
switch s.Code() {
case codes.NotFound:
fmt.Sprintf("Error NotFound %+v\n", err)
case codes.AlreadyExists:
fmt.Sprintf("Error AlreadyExists %+v\n", err)
case codes.PermissionDenied:
fmt.Sprintf("Error PermissionDenied %+v\n", err)
default:
fmt.Sprintf("Error Internal %+v\n", err)
}
}
ここで、status
は gRPC のエラーを処理するためのもの、codes
は gRPC のエラーコードを扱うためのもの。
codes
では以下のように Code
が定義されている。
type Code uint32
const (
OK Code = 0
Canceled Code = 1
Unknown Code = 2
InvalidArgument Code = 3
DeadlineExceeded Code = 4
AlreadyExists Code = 6
PermissionDenied Code = 7
ResourceExhausted Code = 8
FailedPrecondition Code = 9
Aborted Code = 10
OutOfRange Code = 11
Unimplemented Code = 12
Internal Code = 13
Unavailable Code = 14
DataLoss Code = 15
Unauthenticated Code = 16
)
より詳細なエラーを扱いたい場合は、errdetailis
が用意されていて、次のように取得できる。
for _, detail := range s.Details() {
switch t := detail.(type) {
case *errdetails.BadRequest:
for _, violation := range t.GetFieldViolations() {
log.Printf("The %q field was wrong:\n", violation.GetField())
log.Printf("\t%s\n", violation.GetDescription())
}
case *errdetails.LocalizedMessage:
log.Printf("%s:%s", t.GetLocale(), string(t.GetMessage()))
}
}
ちなみに、格納する側は以下のように書く。
import (
"google.golang.org/grpc/status"
"google.golang.org/genproto/googleapis/rpc/errdetails"
)
st := status.New(codes.InvalidArgument, "error message")
st, err := st.WithDetails(
&errdetails.BadRequest{
FieldViolations: []*errdetails.BadRequest_FieldViolation{
&errdetails.BadRequest_FieldViolation{
Field: "Message",
Description: "details of error",
},
},
},
&errdetails.LocalizedMessage{
Locale: "ja",
Message: "詳細なエラー",
},
)
if err != nil {
return err
}
return st.Err()