Browse Source

Merge pull request #2019 from yichengq/266

tools: add etcd-dump-logs
Xiang Li 11 years ago
parent
commit
17401994c0
2 changed files with 118 additions and 0 deletions
  1. 1 0
      build
  2. 117 0
      tools/etcd-dump-logs/main.go

+ 1 - 0
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

+ 117 - 0
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:])
+}