goraftのログファイルをダンプしてみよう


[goraft](https://github.com/goraft/raft)はskydb, etcd, InfluxDB等で利用されているコンセンサスアルゴリズムを実装したライブラリです。今日はgoraftが書き出すlogファイルの解析をしてみましょう

## Protoファイルの確認

ProtocolBuffersの定義ファイルは https://github.com/goraft/raft/blob/master/protobuf/log_entry.proto に記載さています。
gogoprotobuf以外を使う場合optionはエラーになってしまうのでさくっと消して変換してみましょう

log_entry.proto

```protobuf
message LogEntry {
    required uint64 Index=1;
    required uint64 Term=2;
    required string CommandName=3;
    optional bytes Command=4; // for nop-command
}
```

今回はダンプには私が日頃使っているPHPを使うので[PECL ProtocolBuffers](https://github.com/chobie/php-protocolbuffers)と[protoc-gen-php](https://github.com/chobie/protoc-gen-php)(protocも忘れずに！)をセットアップしておいてください。

```bash
protoc --php_out=. logentry.proto
```

### ダンプしてみる

ダンプ用のスクリプトはざっくりと書くとこんな感じです。8byteのサイズ＋デリミタ1byte+Protobufという構造になっているので下記のようにかけば一つ一つのログエントリに分割してparseすることが出来ます。

```php
<?php
require "autoload.php";

$data = file_get_contents($_SERVER['argv'][1]);
$offset = 0;

for (;;) {
    $tmp = sscanf(substr($data, $offset), "%8x\n");
    if (!$tmp) {
        break;
    }
    $length = current($tmp);
    if  ($length < 1) {
        exit;
    }

    $offset += 9;
    $a = substr($data, $offset, $length);
    $log = LogEntry::parseFromString($a);
    var_dump($log);
    $offset += $length;
}
```

それではInfluxDBのlogを`php dump.php /tmp/influxdb/development/raft/log`でダンプしてみましょう。


```php
object(LogEntry)#2 (4) {
  ["Index":protected]=>
  string(1) "1"
  ["Term":protected]=>
  string(1) "0"
  ["CommandName":protected]=>
  string(9) "raft:join"
  ["Command":protected]=>
  string(114) "{"name":"ad0d5b2","connectionString":"http://macbook.local:8090","protobufConnectionString":"macbook.local:8099"}
"
}
object(LogEntry)#4 (4) {
  ["Index":protected]=>
  string(1) "2"
  ["Term":protected]=>
  string(1) "0"
  ["CommandName":protected]=>
  string(10) "add_server"
  ["Command":protected]=>
  string(250) "{"Server":{"Id":0,"RaftName":"ad0d5b2","State":0,"RaftConnectionString":"http://macbook.local:8090","ProtobufConnectionString":"macbook.local:8099","HeartbeatInterval":200000000,"Backoff":1000000000,"MinBackoff":1000000000,"MaxBackoff":10000000000}}
"
}
object(LogEntry)#2 (4) {
  ["Index":protected]=>
  string(1) "3"
  ["Term":protected]=>
  string(1) "0"
  ["CommandName":protected]=>
  string(8) "raft:nop"
  ["Command":protected]=>
  NULL
}
object(LogEntry)#4 (4) {
  ["Index":protected]=>
  string(1) "4"
  ["Term":protected]=>
  string(1) "0"
  ["CommandName":protected]=>
  string(23) "save_cluster_admin_user"
  ["Command":protected]=>
  string(144) "{"user":{"common":{"name":"root","hash":"*************************","is_deleted":false,"cache_key":"root"}}}
"
}
object(LogEntry)#2 (4) {
  ["Index":protected]=>
  string(1) "5"
  ["Term":protected]=>
  string(1) "1"
  ["CommandName":protected]=>
  string(8) "raft:nop"
  ["Command":protected]=>
  NULL
}
object(LogEntry)#2 (4) {
  ["Index":protected]=>
  string(2) "13"
  ["Term":protected]=>
  string(1) "8"
  ["CommandName":protected]=>
  string(9) "create_db"
  ["Command":protected]=>
  string(44) "{"name":"stastics","replicationFactor":1}
"
}
object(LogEntry)#4 (4) {
  ["Index":protected]=>
  string(2) "14"
  ["Term":protected]=>
  string(1) "8"
  ["CommandName":protected]=>
  string(13) "create_shards"
  ["Command":protected]=>
  string(118) "{"Shards":[{"StartTime":"2014-04-17T09:00:00+09:00","EndTime":"2014-04-24T09:00:00+09:00","ServerIds":[1],"Type":1}]}
"
}
object(LogEntry)#4 (4) {
  ["Index":protected]=>
  string(2) "20"
  ["Term":protected]=>
  string(1) "9"
  ["CommandName":protected]=>
  string(8) "raft:nop"
  ["Command":protected]=>
  NULL
}
```

併せてraft/confなどを見ることでどういう情報があるかわかります。
raftのtraceを有効にしたり、ログファイルを読むことでどういう動作をしているのかがよりわかりやすくなるかと思います。