gRPCのchannelz用のCLIツール

以前 [gRPCのChannelzを使ってみた](https://qiita.com/kazegusuri/items/0945f9d805edfb6958ad) というタイトルでgRPCに導入されたchannelzというものを紹介しました。
これを使うことでアプリケーションで動いているgRPCのコネクションがどういう状態かを詳細に知ることができることがわかりました。

[公式のブログ](https://grpc.io/blog/a_short_introduction_to_channelz)でも紹介され、そこでは [grpc-zpages](https://github.com/grpc/grpc-experiments/tree/master/gdebug) と呼ばれるwebベースのchannelzのフロントエンドが公開されていました。自分も似たようなWebベースのものを作ろうと思っていて、社内にある複数のgRPCサービスをまとめて見られるダッシュボードのようなもの(まだない)にchannelzと連携して表示しようとしています。

今回は手元ですぐに状況を知りたいことが多くなってきたので簡単に作れるCLIとして[channelzcli](https://github.com/kazegusuri/channelzcli)というものを作ってみました。

## channelzcli

channelzcli は channelz serviceから情報を取得して表示するツールです。
まだまださっと作ったものなのでエラーハンドリングとかが雑ですが、channelz serviceから取れる情報はだいたい表示できるようになっているかなと思います。

コマンドとしては3種類で表示方法や内容が異なるだけで情報のソース(API)は同じです。

* list: 状態を全体的にテーブル形式で見る
* describe: 特定のものの詳細を見る
* tree: 全体をツリー上に見る

各コマンドでは対象として `channel` と `server` を指定できます。
概念的には `Socket` や `Subchannel` などもあるのでdescribeなどで詳細表示できるように都度足していきます。

* channel: アプリケーション自体が直接生成したchannel(TopChannel)
* server: アプリケーションのgRPCサーバ

### List

雰囲気的にはkubectl getみたいなものです。
gRPCではchannelの中にchannelがあり、それぞれ毎にstateなどを持っているのでざっくりどういう状態か知るために作りました。
Subchannel毎の内訳もここに表示したかったので、APIコールがN+1になるのでどうしようかなと考え中。

```
$ channelzcli -k --addr localhost:8000 list channel
ID	Name                                    	State	Channel	SubChannel	Calls	Success	Fail	LastCall
1	spanner.googleapis.com:443              	READY	0      	1         	6372  	6362  	12    	9s      
2	spanner.googleapis.com:443              	READY	0      	1         	6442  	6433  	11    	2m      
3	spanner.googleapis.com:443              	READY	0      	1         	6306  	6293  	17    	1m      
4	spanner.googleapis.com:443              	READY	0      	1         	6865  	6849  	21    	54s     
28	pubsub.googleapis.com:443               	READY	0      	16        	0     	0     	0     	none    
29	pubsub.googleapis.com:443               	READY	0      	16        	80    	80    	0     	12h     
```

```
$ channelzcli -k --addr localhost:8000 list server
ID	Name	LocalAddr	Calls	Success	Fail	LastCall
31	<none>	<none>      	2264  	2262  	1     	410ms
35	<none>	[::]:5000   	1732  	1090  	642   	10h
```

### Describe

describeはとりあえず取得できる情報全部表示するかという感じで作りました。
TraceのEventはchannelに発生したイベントが出てくるので問題を把握するのにも便利です。

```
$ channelcli -k --addr localhost:18000 describe channel 29
Name:     	pubsub.googleapis.com:443
ChannelID:	29
State:    	READY
Target:   	pubsub.googleapis.com:443
Calls:
  Started:        	80
  Succeeded:      	80
  Failed:         	0
  LastCallStarted:	2018-12-01 21:11:17.64129022 +0000 UTC
Socket:   	<none>
Channels:   	<none>
Subchannels:
  ID	Name	State	Start	Succeeded	Failed
  71		READY	5	5	0
  58		READY	5	5	0
  64		READY	5	5	0
  70		READY	5	5	0
  66		READY	5	5	0
  68		READY	5	5	0
  38		READY	5	5	0
  56		READY	5	5	0
  57		READY	5	5	0
  62		READY	5	5	0
  69		READY	5	5	0
  63		READY	5	5	0
  61		READY	5	5	0
  65		READY	5	5	0
  59		READY	5	5	0
  67		READY	5	5	0
Trace:
  NumEvents:	22
  CreationTimestamp:	2018-11-30 11:16:06.335742037 +0000 UTC
  Events
    Severity	Description                                                                     	Timestamp
    INFO	Channel Created                                                                 	2018-11-30 11:16:06.335743742 +0000 UTC
    INFO	Resolver returns a non-empty address list (previous one was empty) "pubsub.googleapis.com:443"	2018-11-30 11:16:06.427269951 +0000 UTC
    INFO	Subchannel(id:38) created                                                       	2018-11-30 11:16:06.525993125 +0000 UTC
    INFO	Subchannel(id:56) created                                                       	2018-11-30 11:16:06.526069714 +0000 UTC
    INFO	Channel Connectivity change to CONNECTING                                       	2018-11-30 11:16:06.526084142 +0000 UTC
    INFO	Subchannel(id:57) created                                                       	2018-11-30 11:16:06.526261462 +0000 UTC
    INFO	Subchannel(id:58) created                                                       	2018-11-30 11:16:06.526275698 +0000 UTC
    INFO	Subchannel(id:59) created                                                       	2018-11-30 11:16:06.526310408 +0000 UTC
    INFO	Subchannel(id:61) created                                                       	2018-11-30 11:16:06.526424564 +0000 UTC
    INFO	Subchannel(id:62) created                                                       	2018-11-30 11:16:06.526442884 +0000 UTC
    INFO	Subchannel(id:63) created                                                       	2018-11-30 11:16:06.526450184 +0000 UTC
    INFO	Subchannel(id:64) created                                                       	2018-11-30 11:16:06.52646022 +0000 UTC
    INFO	Subchannel(id:65) created                                                       	2018-11-30 11:16:06.52647477 +0000 UTC
    INFO	Subchannel(id:66) created                                                       	2018-11-30 11:16:06.526482334 +0000 UTC
    INFO	Subchannel(id:67) created                                                       	2018-11-30 11:16:06.526566127 +0000 UTC
    INFO	Subchannel(id:68) created                                                       	2018-11-30 11:16:06.526575083 +0000 UTC
    INFO	Subchannel(id:69) created                                                       	2018-11-30 11:16:06.526583642 +0000 UTC
    INFO	Subchannel(id:70) created                                                       	2018-11-30 11:16:06.526597817 +0000 UTC
    INFO	Subchannel(id:71) created                                                       	2018-11-30 11:16:06.526631882 +0000 UTC
    INFO	Channel Connectivity change to READY                                            	2018-11-30 11:16:07.438772142 +0000 UTC
    INFO	Channel Connectivity change to CONNECTING                                       	2018-11-30 12:40:09.169893863 +0000 UTC
    INFO	Channel Connectivity change to READY                                            	2018-11-30 12:40:12.967264812 +0000 UTC
```

### Tree

treeコマンドはchannelなどの情報をツリー上に視覚的にわかりやすく表示するために作りました。
channelはchannelの中にsubchannelがあって更にその中にsubchannelがあるとかもありえるので、describeなどでは表示が厳しそうだからです。

```
pubsub.googleapis.com:443 (ID:28) [READY]
  [Calls] Started:0, Succeeded:0, Failed:0, Last:none
  [Subchannels]
    |-- pubsub.googleapis.com:443 (ID:52) [READY]
          [Calls]: Started:0, Succeeded:0, Failed:0, Last:none
          [Socket] ID:22840, Name:, RemoteName:, Local:[10.0.0.xx]:39306 Remote:[172.217.27.xx]:443
    |-- pubsub.googleapis.com:443 (ID:45) [READY]
          [Calls]: Started:0, Succeeded:0, Failed:0, Last:none
          [Socket] ID:22844, Name:, RemoteName:, Local:[10.0.0.xx]:42054 Remote:[216.58.197.xx]:443
    |-- pubsub.googleapis.com:443 (ID:53) [READY]
          [Calls]: Started:0, Succeeded:0, Failed:0, Last:none
          [Socket] ID:22837, Name:, RemoteName:, Local:[10.0.0.xx]:49570 Remote:[172.217.31.xx]:443
    |-- pubsub.googleapis.com:443 (ID:55) [READY]
          [Calls]: Started:0, Succeeded:0, Failed:0, Last:none
          [Socket] ID:22841, Name:, RemoteName:, Local:[10.0.0.xx]:45626 Remote:[216.58.197.xx]:443
    |-- pubsub.googleapis.com:443 (ID:46) [READY]
          [Calls]: Started:0, Succeeded:0, Failed:0, Last:none
          [Socket] ID:22850, Name:, RemoteName:, Local:[10.0.0.xx]:54362 Remote:[216.58.197.xx]:443
    ....

pubsub.googleapis.com:443 (ID:29) [READY]
  [Calls] Started:80, Succeeded:80, Failed:0, Last:12h
  [Subchannels]
    |-- pubsub.googleapis.com:443 (ID:56) [READY]
          [Calls]: Started:5, Succeeded:5, Failed:0, Last:12h
          [Socket] ID:22862, Name:, RemoteName:, Local:[10.0.0.xx]51096 Remote:[172.217.31.xx]:443
    |-- pubsub.googleapis.com:443 (ID:63) [READY]
          [Calls]: Started:5, Succeeded:5, Failed:0, Last:12h
          [Socket] ID:22856, Name:, RemoteName:, Local:[10.0.0.xx]:57712 Remote:[172.217.161.xx]:443
    |-- pubsub.googleapis.com:443 (ID:57) [READY]
          [Calls]: Started:5, Succeeded:5, Failed:0, Last:12h
          [Socket] ID:22865, Name:, RemoteName:, Local:[10.0.0.xx]:47202 Remote:[172.217.25.xx]:443
    |-- pubsub.googleapis.com:443 (ID:65) [READY]
          [Calls]: Started:5, Succeeded:5, Failed:0, Last:12h
          [Socket] ID:22863, Name:, RemoteName:, Local:[10.0.0.xx]:47198 Remote:[172.217.25.xx]:443
  ....
```

### その他

機能的にほしいのは `top` コマンドみたいに定期的に更新して変化を見られるものとか作りたいなとか考えてます。
あとはリクエスト数と成功数からreq/sとか成功率とかも計算できるはずなのでそのあたりも表示できると嬉しそうです。