以前の記事からGo言語でサーバーアプリを自分で書き始めて、エラーハンドリングが必要になった。
エラーについては下記記事が参考になった。
qiita.com
この記事のように自分で作ったエラーならいいんだけど、大抵は外部のライブラリのエラーではまる。
Cloud PUB/SUBのライブラリ("cloud.google.com/go/pubsub")は、内部ではgRPCでリクエストをしており、エラーもこのライブラリのが返ってくる。
import "google.golang.org/grpc"
下記のようなコードで、起動時にはトピックがあったけど、その後に消されてしまったとき、ps.publishHandlerが返すエラーを特定したかった。
とりあえず、一番汚いやり方、err.Error()で帰ってきた文字列をstrings.Indexで単純に比較。これでも「今は」動く。
go func() { err = ps.publishHandler(buf[:n]) if err != nil { log.Printf("publishHandler err: %v", err) log.Printf("err type:%v", reflect.TypeOf(err)) log.Printf("err.Error():%v", err.Error()) log.Println(strings.Index(err.Error(), "rpc error: code = NotFound desc = Resource not found")) } }()
実行すると
2017/03/28 01:51:19 publishHandler err: rpc error: code = NotFound desc = Resource not found (resource=ml30gen9-bizocean).
2017/03/28 01:51:19 err type:*grpc.rpcError
2017/03/28 01:51:19 err.Error():rpc error: code = NotFound desc = Resource not found (resource=ml30gen9-bizocean).
2017/03/28 01:51:19 0
と0が帰ってきて判別はできる。含まれない場合は-1。
エラーの型には*grpc.rpcErrorが返ってくる。
単純にこいつと型比較しようとするとrpcErrorという名前から分かるように、最初が小文字なので外部から呼び出せなくてむかつく。
なぜ外から触れないようになってるのか、私のGo力(ごーぢから)ではソースコードから読みとれなかった。
grpcには下記のような関数があり、エラーを渡せば、コードが返ってくる。
func Code(err error) codes.Code
が、他のエラーも返ってくることもあり、その場合エラーになるのでまず型判定が必要。
app/main.go:212: cannot convert err (type error) to type codes.Code
reflect.TypeOfを使って
src/reflect/type.go - The Go Programming Language
返ってくるTypeをstringにして比較してみる。
if reflect.TypeOf(err).String() == "*grpc.rpcError" { //process }
これは成功。
でも文字列比較はダサい。
だがさっき書いた用に呼び出して型を調べようとするとエラーになる。文字列化もできない。
if reflect.TypeOf(err) == grpc.rpcError { //process } if reflect.TypeOf(err) == *grpc.rpcError { //process } log.Printf("TypeOf(*grpc.rpcError):v", reflect.TypeOf(grpc.rpcError).String())
3つともこういうエラーが出る。
app/main.go:209: cannot refer to unexported name grpc.rpcError
今のところ下記のダサい方法しかない
if reflect.TypeOf(err).String() == "*grpc.rpcError" { //process }||< 誰か助けてー