GAミント至上主義

Web Monomaniacal Developer.

Go言語でgRPCのエラーと戦って負ける

以前の記事からGo言語でサーバーアプリを自分で書き始めて、エラーハンドリングが必要になった。


エラーについては下記記事が参考になった。
qiita.com


この記事のように自分で作ったエラーならいいんだけど、大抵は外部のライブラリのエラーではまる。

Cloud PUB/SUBのライブラリ("cloud.google.com/go/pubsub")は、内部ではgRPCでリクエストをしており、エラーもこのライブラリのが返ってくる。


godoc.org


godoc.org

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 - GoDoc

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
}||<


誰か助けてー