From 8b0c7bf652a3b20594215feb2ee04dcfbf9b784a Mon Sep 17 00:00:00 2001 From: Yicheng Qin Date: Tue, 30 Dec 2014 14:58:14 -0800 Subject: [PATCH] tools: add etcd-dump-logs The tool can dump the log from data directory. It helps develop and debug. --- build | 1 + tools/etcd-dump-logs/main.go | 117 +++++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 tools/etcd-dump-logs/main.go diff --git a/build b/build index 3cbce5bbe..0309a0dfe 100755 --- a/build +++ b/build @@ -14,3 +14,4 @@ eval $(go env) go build -o bin/etcd ${REPO_PATH} CGO_ENABLED=0 go build -a -ldflags '-s' -o bin/etcdctl ${REPO_PATH}/etcdctl go build -o bin/etcd-migrate ${REPO_PATH}/migrate/cmd/etcd-migrate +go build -o bin/etcd-dump-logs ${REPO_PATH}/tools/etcd-dump-logs diff --git a/tools/etcd-dump-logs/main.go b/tools/etcd-dump-logs/main.go new file mode 100644 index 000000000..db87806ec --- /dev/null +++ b/tools/etcd-dump-logs/main.go @@ -0,0 +1,117 @@ +package main + +import ( + "flag" + "fmt" + "log" + "path" + "time" + + "github.com/coreos/etcd/etcdserver/etcdserverpb" + "github.com/coreos/etcd/pkg/pbutil" + "github.com/coreos/etcd/pkg/types" + "github.com/coreos/etcd/raft/raftpb" + "github.com/coreos/etcd/snap" + "github.com/coreos/etcd/wal" +) + +func main() { + from := flag.String("data-dir", "", "") + flag.Parse() + if *from == "" { + log.Fatal("Must provide -data-dir flag") + } + + ss := snap.New(snapDir(*from)) + snapshot, err := ss.Load() + var index uint64 + switch err { + case nil: + index = snapshot.Metadata.Index + nodes := genIDSlice(snapshot.Metadata.ConfState.Nodes) + fmt.Printf("Snapshot:\nterm=%d index=%d nodes=%s\n", + snapshot.Metadata.Term, index, nodes) + case snap.ErrNoSnapshot: + fmt.Printf("Snapshot:\nempty\n") + default: + log.Fatalf("Failed loading snapshot: %v", err) + } + + w, err := wal.Open(walDir(*from), index+1) + if err != nil { + log.Fatalf("Failed opening WAL: %v", err) + } + wmetadata, state, ents, err := w.ReadAll() + w.Close() + if err != nil { + log.Fatalf("Failed reading WAL: %v", err) + } + id, cid := parseWALMetadata(wmetadata) + vid := types.ID(state.Vote) + fmt.Printf("WAL metadata:\nnodeID=%s clusterID=%s term=%d commitIndex=%d vote=%s\n", + id, cid, state.Term, state.Commit, vid) + + fmt.Printf("WAL entries:\n") + fmt.Printf("lastIndex=%d\n", ents[len(ents)-1].Index) + fmt.Printf("%4s\t%10s\ttype\tdata\n", "term", "index") + for _, e := range ents { + msg := fmt.Sprintf("%4d\t%10d", e.Term, e.Index) + switch e.Type { + case raftpb.EntryNormal: + msg = fmt.Sprintf("%s\tnorm", msg) + var r etcdserverpb.Request + if err := r.Unmarshal(e.Data); err != nil { + msg = fmt.Sprintf("%s\t???", msg) + break + } + switch r.Method { + case "": + msg = fmt.Sprintf("%s\tnoop", msg) + case "SYNC": + msg = fmt.Sprintf("%s\tmethod=SYNC time=%q", msg, time.Unix(0, r.Time)) + case "QGET", "DELETE": + msg = fmt.Sprintf("%s\tmethod=%s path=%s", msg, r.Method, excerpt(r.Path, 64, 64)) + default: + msg = fmt.Sprintf("%s\tmethod=%s path=%s val=%s", msg, r.Method, excerpt(r.Path, 64, 64), excerpt(r.Val, 128, 0)) + } + case raftpb.EntryConfChange: + msg = fmt.Sprintf("%s\tconf", msg) + var r raftpb.ConfChange + if err := r.Unmarshal(e.Data); err != nil { + msg = fmt.Sprintf("%s\t???", msg) + } else { + msg = fmt.Sprintf("%s\tmethod=%s id=%s", msg, r.Type, types.ID(r.NodeID)) + } + } + fmt.Println(msg) + } +} + +func walDir(dataDir string) string { return path.Join(dataDir, "wal") } + +func snapDir(dataDir string) string { return path.Join(dataDir, "snap") } + +func parseWALMetadata(b []byte) (id, cid types.ID) { + var metadata etcdserverpb.Metadata + pbutil.MustUnmarshal(&metadata, b) + id = types.ID(metadata.NodeID) + cid = types.ID(metadata.ClusterID) + return +} + +func genIDSlice(a []uint64) []types.ID { + ids := make([]types.ID, len(a)) + for i, id := range a { + ids[i] = types.ID(id) + } + return ids +} + +// excerpt replaces middle part with ellipsis and returns a double-quoted +// string safely escaped with Go syntax. +func excerpt(str string, pre, suf int) string { + if pre+suf > len(str) { + return fmt.Sprintf("%q", str) + } + return fmt.Sprintf("%q...%q", str[:pre], str[len(str)-suf:]) +}