main.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. // Copyright 2018 The etcd Authors
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package main
  15. import (
  16. "flag"
  17. "fmt"
  18. "log"
  19. "path/filepath"
  20. "strings"
  21. "time"
  22. "github.com/coreos/etcd/etcdserver/api/snap"
  23. "github.com/coreos/etcd/etcdserver/etcdserverpb"
  24. "github.com/coreos/etcd/pkg/pbutil"
  25. "github.com/coreos/etcd/pkg/types"
  26. "github.com/coreos/etcd/raft/raftpb"
  27. "github.com/coreos/etcd/wal"
  28. "github.com/coreos/etcd/wal/walpb"
  29. "go.uber.org/zap"
  30. )
  31. func main() {
  32. snapfile := flag.String("start-snap", "", "The base name of snapshot file to start dumping")
  33. index := flag.Uint64("start-index", 0, "The index to start dumping")
  34. entrytype := flag.String("entry-type", "", `If set, filters output by entry type. Must be one or more than one of:
  35. ConfigChange, Normal, Request, InternalRaftRequest,
  36. IRRRange, IRRPut, IRRDeleteRange, IRRTxn,
  37. IRRCompaction, IRRLeaseGrant, IRRLeaseRevoke`)
  38. flag.Parse()
  39. if len(flag.Args()) != 1 {
  40. log.Fatalf("Must provide data-dir argument (got %+v)", flag.Args())
  41. }
  42. dataDir := flag.Args()[0]
  43. if *snapfile != "" && *index != 0 {
  44. log.Fatal("start-snap and start-index flags cannot be used together.")
  45. }
  46. var (
  47. walsnap walpb.Snapshot
  48. snapshot *raftpb.Snapshot
  49. err error
  50. )
  51. isIndex := *index != 0
  52. if isIndex {
  53. fmt.Printf("Start dumping log entries from index %d.\n", *index)
  54. walsnap.Index = *index
  55. } else {
  56. if *snapfile == "" {
  57. ss := snap.New(zap.NewExample(), snapDir(dataDir))
  58. snapshot, err = ss.Load()
  59. } else {
  60. snapshot, err = snap.Read(zap.NewExample(), filepath.Join(snapDir(dataDir), *snapfile))
  61. }
  62. switch err {
  63. case nil:
  64. walsnap.Index, walsnap.Term = snapshot.Metadata.Index, snapshot.Metadata.Term
  65. nodes := genIDSlice(snapshot.Metadata.ConfState.Nodes)
  66. fmt.Printf("Snapshot:\nterm=%d index=%d nodes=%s\n",
  67. walsnap.Term, walsnap.Index, nodes)
  68. case snap.ErrNoSnapshot:
  69. fmt.Printf("Snapshot:\nempty\n")
  70. default:
  71. log.Fatalf("Failed loading snapshot: %v", err)
  72. }
  73. fmt.Println("Start dupmping log entries from snapshot.")
  74. }
  75. w, err := wal.OpenForRead(zap.NewExample(), walDir(dataDir), walsnap)
  76. if err != nil {
  77. log.Fatalf("Failed opening WAL: %v", err)
  78. }
  79. wmetadata, state, ents, err := w.ReadAll()
  80. w.Close()
  81. if err != nil && (!isIndex || err != wal.ErrSnapshotNotFound) {
  82. log.Fatalf("Failed reading WAL: %v", err)
  83. }
  84. id, cid := parseWALMetadata(wmetadata)
  85. vid := types.ID(state.Vote)
  86. fmt.Printf("WAL metadata:\nnodeID=%s clusterID=%s term=%d commitIndex=%d vote=%s\n",
  87. id, cid, state.Term, state.Commit, vid)
  88. fmt.Printf("WAL entries:\n")
  89. fmt.Printf("lastIndex=%d\n", ents[len(ents)-1].Index)
  90. fmt.Printf("%4s\t%10s\ttype\tdata\n", "term", "index")
  91. listEntriesType(*entrytype, ents)
  92. }
  93. func walDir(dataDir string) string { return filepath.Join(dataDir, "member", "wal") }
  94. func snapDir(dataDir string) string { return filepath.Join(dataDir, "member", "snap") }
  95. func parseWALMetadata(b []byte) (id, cid types.ID) {
  96. var metadata etcdserverpb.Metadata
  97. pbutil.MustUnmarshal(&metadata, b)
  98. id = types.ID(metadata.NodeID)
  99. cid = types.ID(metadata.ClusterID)
  100. return id, cid
  101. }
  102. func genIDSlice(a []uint64) []types.ID {
  103. ids := make([]types.ID, len(a))
  104. for i, id := range a {
  105. ids[i] = types.ID(id)
  106. }
  107. return ids
  108. }
  109. // excerpt replaces middle part with ellipsis and returns a double-quoted
  110. // string safely escaped with Go syntax.
  111. func excerpt(str string, pre, suf int) string {
  112. if pre+suf > len(str) {
  113. return fmt.Sprintf("%q", str)
  114. }
  115. return fmt.Sprintf("%q...%q", str[:pre], str[len(str)-suf:])
  116. }
  117. type EntryFilter func(e raftpb.Entry) (bool, string)
  118. // The 9 pass functions below takes the raftpb.Entry and return if the entry should be printed and the type of entry,
  119. // the type of the entry will used in the following print function
  120. func passConfChange(entry raftpb.Entry) (bool, string) {
  121. return entry.Type == raftpb.EntryConfChange, "ConfigChange"
  122. }
  123. func passInternalRaftRequest(entry raftpb.Entry) (bool, string) {
  124. var rr etcdserverpb.InternalRaftRequest
  125. return entry.Type == raftpb.EntryNormal && rr.Unmarshal(entry.Data) == nil, "InternalRaftRequest"
  126. }
  127. func passUnknownNormal(entry raftpb.Entry) (bool, string) {
  128. var rr1 etcdserverpb.Request
  129. var rr2 etcdserverpb.InternalRaftRequest
  130. return (entry.Type == raftpb.EntryNormal) && (rr1.Unmarshal(entry.Data) != nil) && (rr2.Unmarshal(entry.Data) != nil), "UnknownNormal"
  131. }
  132. func passIRRRange(entry raftpb.Entry) (bool, string) {
  133. var rr etcdserverpb.InternalRaftRequest
  134. return entry.Type == raftpb.EntryNormal && rr.Unmarshal(entry.Data) == nil && rr.Range != nil, "InternalRaftRequest"
  135. }
  136. func passIRRPut(entry raftpb.Entry) (bool, string) {
  137. var rr etcdserverpb.InternalRaftRequest
  138. return entry.Type == raftpb.EntryNormal && rr.Unmarshal(entry.Data) == nil && rr.Put != nil, "InternalRaftRequest"
  139. }
  140. func passIRRDeleteRange(entry raftpb.Entry) (bool, string) {
  141. var rr etcdserverpb.InternalRaftRequest
  142. return entry.Type == raftpb.EntryNormal && rr.Unmarshal(entry.Data) == nil && rr.DeleteRange != nil, "InternalRaftRequest"
  143. }
  144. func passIRRTxn(entry raftpb.Entry) (bool, string) {
  145. var rr etcdserverpb.InternalRaftRequest
  146. return entry.Type == raftpb.EntryNormal && rr.Unmarshal(entry.Data) == nil && rr.Txn != nil, "InternalRaftRequest"
  147. }
  148. func passIRRCompaction(entry raftpb.Entry) (bool, string) {
  149. var rr etcdserverpb.InternalRaftRequest
  150. return entry.Type == raftpb.EntryNormal && rr.Unmarshal(entry.Data) == nil && rr.Compaction != nil, "InternalRaftRequest"
  151. }
  152. func passIRRLeaseGrant(entry raftpb.Entry) (bool, string) {
  153. var rr etcdserverpb.InternalRaftRequest
  154. return entry.Type == raftpb.EntryNormal && rr.Unmarshal(entry.Data) == nil && rr.LeaseGrant != nil, "InternalRaftRequest"
  155. }
  156. func passIRRLeaseRevoke(entry raftpb.Entry) (bool, string) {
  157. var rr etcdserverpb.InternalRaftRequest
  158. return entry.Type == raftpb.EntryNormal && rr.Unmarshal(entry.Data) == nil && rr.LeaseRevoke != nil, "InternalRaftRequest"
  159. }
  160. func passRequest(entry raftpb.Entry) (bool, string) {
  161. var rr1 etcdserverpb.Request
  162. var rr2 etcdserverpb.InternalRaftRequest
  163. return entry.Type == raftpb.EntryNormal && rr1.Unmarshal(entry.Data) == nil && rr2.Unmarshal(entry.Data) != nil, "Request"
  164. }
  165. type EntryPrinter func(e raftpb.Entry)
  166. // The 4 print functions below print the entry format based on there types
  167. // printInternalRaftRequest is used to print entry information for IRRRange, IRRPut,
  168. // IRRDeleteRange and IRRTxn entries
  169. func printInternalRaftRequest(entry raftpb.Entry) {
  170. var rr etcdserverpb.InternalRaftRequest
  171. if err := rr.Unmarshal(entry.Data); err == nil {
  172. fmt.Printf("%4d\t%10d\tnorm\t%s\n", entry.Term, entry.Index, rr.String())
  173. }
  174. }
  175. func printUnknownNormal(entry raftpb.Entry) {
  176. fmt.Printf("%4d\t%10d\tnorm\t???\n", entry.Term, entry.Index)
  177. }
  178. func printConfChange(entry raftpb.Entry) {
  179. fmt.Printf("%4d\t%10d", entry.Term, entry.Index)
  180. fmt.Printf("\tconf")
  181. var r raftpb.ConfChange
  182. if err := r.Unmarshal(entry.Data); err != nil {
  183. fmt.Printf("\t???\n")
  184. } else {
  185. fmt.Printf("\tmethod=%s id=%s\n", r.Type, types.ID(r.NodeID))
  186. }
  187. }
  188. func printRequest(entry raftpb.Entry) {
  189. var r etcdserverpb.Request
  190. if err := r.Unmarshal(entry.Data); err == nil {
  191. fmt.Printf("%4d\t%10d\tnorm", entry.Term, entry.Index)
  192. switch r.Method {
  193. case "":
  194. fmt.Printf("\tnoop\n")
  195. case "SYNC":
  196. fmt.Printf("\tmethod=SYNC time=%q\n", time.Unix(0, r.Time))
  197. case "QGET", "DELETE":
  198. fmt.Printf("\tmethod=%s path=%s\n", r.Method, excerpt(r.Path, 64, 64))
  199. default:
  200. fmt.Printf("\tmethod=%s path=%s val=%s\n", r.Method, excerpt(r.Path, 64, 64), excerpt(r.Val, 128, 0))
  201. }
  202. }
  203. }
  204. // evaluateEntrytypeFlag evaluates entry-type flag and choose proper filter/filters to filter entries
  205. func evaluateEntrytypeFlag(entrytype string) []EntryFilter {
  206. var entrytypelist []string
  207. if entrytype != "" {
  208. entrytypelist = strings.Split(entrytype, ",")
  209. }
  210. validRequest := map[string][]EntryFilter{"ConfigChange": {passConfChange},
  211. "Normal": {passInternalRaftRequest, passRequest, passUnknownNormal},
  212. "Request": {passRequest},
  213. "InternalRaftRequest": {passInternalRaftRequest},
  214. "IRRRange": {passIRRRange},
  215. "IRRPut": {passIRRPut},
  216. "IRRDeleteRange": {passIRRDeleteRange},
  217. "IRRTxn": {passIRRTxn},
  218. "IRRCompaction": {passIRRCompaction},
  219. "IRRLeaseGrant": {passIRRLeaseGrant},
  220. "IRRLeaseRevoke": {passIRRLeaseRevoke},
  221. }
  222. filters := make([]EntryFilter, 0)
  223. if len(entrytypelist) == 0 {
  224. filters = append(filters, passInternalRaftRequest)
  225. filters = append(filters, passRequest)
  226. filters = append(filters, passUnknownNormal)
  227. filters = append(filters, passConfChange)
  228. }
  229. for _, et := range entrytypelist {
  230. if f, ok := validRequest[et]; ok {
  231. filters = append(filters, f...)
  232. } else {
  233. log.Printf(`[%+v] is not a valid entry-type, ignored.
  234. Please set entry-type to one or more of the following:
  235. ConfigChange, Normal, Request, InternalRaftRequest,
  236. IRRRange, IRRPut, IRRDeleteRange, IRRTxn,
  237. IRRCompaction, IRRLeaseGrant, IRRLeaseRevoke`, et)
  238. }
  239. }
  240. return filters
  241. }
  242. // listEntriesType filters and prints entries based on the entry-type flag,
  243. func listEntriesType(entrytype string, ents []raftpb.Entry) {
  244. entryFilters := evaluateEntrytypeFlag(entrytype)
  245. printerMap := map[string]EntryPrinter{"InternalRaftRequest": printInternalRaftRequest,
  246. "Request": printRequest,
  247. "ConfigChange": printConfChange,
  248. "UnknownNormal": printUnknownNormal}
  249. cnt := 0
  250. for _, e := range ents {
  251. passed := false
  252. currtype := ""
  253. for _, filter := range entryFilters {
  254. passed, currtype = filter(e)
  255. if passed {
  256. cnt++
  257. break
  258. }
  259. }
  260. if passed {
  261. printer := printerMap[currtype]
  262. printer(e)
  263. }
  264. }
  265. fmt.Printf("\nEntry types (%s) count is : %d", entrytype, cnt)
  266. }