github.com/yinchengtsinghua/golang-Eos-dpos-Ethereum@v0.0.0-20190121132951-92cc4225ed8e/cmd/geth/monitorcmd.go (about) 1 2 //此源码被清华学神尹成大魔王专业翻译分析并修改 3 //尹成QQ77025077 4 //尹成微信18510341407 5 //尹成所在QQ群721929980 6 //尹成邮箱 yinc13@mails.tsinghua.edu.cn 7 //尹成毕业于清华大学,微软区块链领域全球最有价值专家 8 //https://mvp.microsoft.com/zh-cn/PublicProfile/4033620 9 //版权所有2015 Go Ethereum作者 10 //此文件是Go以太坊的一部分。 11 // 12 //Go以太坊是免费软件:您可以重新发布和/或修改它 13 //根据GNU通用公共许可证的条款 14 //自由软件基金会,或者许可证的第3版,或者 15 //(由您选择)任何更高版本。 16 // 17 //Go以太坊的分布希望它会有用, 18 //但没有任何保证;甚至没有 19 //适销性或特定用途的适用性。见 20 //GNU通用公共许可证了解更多详细信息。 21 // 22 //你应该已经收到一份GNU通用公共许可证的副本 23 //一起去以太坊吧。如果没有,请参见<http://www.gnu.org/licenses/>。 24 25 package main 26 27 import ( 28 "fmt" 29 "math" 30 "reflect" 31 "runtime" 32 "sort" 33 "strings" 34 "time" 35 36 "github.com/ethereum/go-ethereum/cmd/utils" 37 "github.com/ethereum/go-ethereum/node" 38 "github.com/ethereum/go-ethereum/rpc" 39 "github.com/gizak/termui" 40 "gopkg.in/urfave/cli.v1" 41 ) 42 43 var ( 44 monitorCommandAttachFlag = cli.StringFlag{ 45 Name: "attach", 46 Value: node.DefaultIPCEndpoint(clientIdentifier), 47 Usage: "API endpoint to attach to", 48 } 49 monitorCommandRowsFlag = cli.IntFlag{ 50 Name: "rows", 51 Value: 5, 52 Usage: "Maximum rows in the chart grid", 53 } 54 monitorCommandRefreshFlag = cli.IntFlag{ 55 Name: "refresh", 56 Value: 3, 57 Usage: "Refresh interval in seconds", 58 } 59 monitorCommand = cli.Command{ 60 Action: utils.MigrateFlags(monitor), //跟踪迁移进度 61 Name: "monitor", 62 Usage: "Monitor and visualize node metrics", 63 ArgsUsage: " ", 64 Category: "MONITOR COMMANDS", 65 Description: ` 66 The Geth monitor is a tool to collect and visualize various internal metrics 67 gathered by the node, supporting different chart types as well as the capacity 68 to display multiple metrics simultaneously. 69 `, 70 Flags: []cli.Flag{ 71 monitorCommandAttachFlag, 72 monitorCommandRowsFlag, 73 monitorCommandRefreshFlag, 74 }, 75 } 76 ) 77 78 //Monitor为请求的度量启动一个基于终端UI的监视工具。 79 func monitor(ctx *cli.Context) error { 80 var ( 81 client *rpc.Client 82 err error 83 ) 84 //通过IPC或RPC连接到以太坊节点 85 endpoint := ctx.String(monitorCommandAttachFlag.Name) 86 if client, err = dialRPC(endpoint); err != nil { 87 utils.Fatalf("Unable to attach to geth node: %v", err) 88 } 89 defer client.Close() 90 91 //检索所有可用的度量并解析用户模式 92 metrics, err := retrieveMetrics(client) 93 if err != nil { 94 utils.Fatalf("Failed to retrieve system metrics: %v", err) 95 } 96 monitored := resolveMetrics(metrics, ctx.Args()) 97 if len(monitored) == 0 { 98 list := expandMetrics(metrics, "") 99 sort.Strings(list) 100 101 if len(list) > 0 { 102 utils.Fatalf("No metrics specified.\n\nAvailable:\n - %s", strings.Join(list, "\n - ")) 103 } else { 104 utils.Fatalf("No metrics collected by geth (--%s).\n", utils.MetricsEnabledFlag.Name) 105 } 106 } 107 sort.Strings(monitored) 108 if cols := len(monitored) / ctx.Int(monitorCommandRowsFlag.Name); cols > 6 { 109 utils.Fatalf("Requested metrics (%d) spans more that 6 columns:\n - %s", len(monitored), strings.Join(monitored, "\n - ")) 110 } 111 //创建和配置图表用户界面默认值 112 if err := termui.Init(); err != nil { 113 utils.Fatalf("Unable to initialize terminal UI: %v", err) 114 } 115 defer termui.Close() 116 117 rows := len(monitored) 118 if max := ctx.Int(monitorCommandRowsFlag.Name); rows > max { 119 rows = max 120 } 121 cols := (len(monitored) + rows - 1) / rows 122 for i := 0; i < rows; i++ { 123 termui.Body.AddRows(termui.NewRow()) 124 } 125 //创建每个单独的数据图表 126 footer := termui.NewPar("") 127 footer.Block.Border = true 128 footer.Height = 3 129 130 charts := make([]*termui.LineChart, len(monitored)) 131 units := make([]int, len(monitored)) 132 data := make([][]float64, len(monitored)) 133 for i := 0; i < len(monitored); i++ { 134 charts[i] = createChart((termui.TermHeight() - footer.Height) / rows) 135 row := termui.Body.Rows[i%rows] 136 row.Cols = append(row.Cols, termui.NewCol(12/cols, 0, charts[i])) 137 } 138 termui.Body.AddRows(termui.NewRow(termui.NewCol(12, 0, footer))) 139 140 refreshCharts(client, monitored, data, units, charts, ctx, footer) 141 termui.Body.Align() 142 termui.Render(termui.Body) 143 144 //监视各种系统事件,并定期刷新图表 145 termui.Handle("/sys/kbd/C-c", func(termui.Event) { 146 termui.StopLoop() 147 }) 148 termui.Handle("/sys/wnd/resize", func(termui.Event) { 149 termui.Body.Width = termui.TermWidth() 150 for _, chart := range charts { 151 chart.Height = (termui.TermHeight() - footer.Height) / rows 152 } 153 termui.Body.Align() 154 termui.Render(termui.Body) 155 }) 156 go func() { 157 tick := time.NewTicker(time.Duration(ctx.Int(monitorCommandRefreshFlag.Name)) * time.Second) 158 for range tick.C { 159 if refreshCharts(client, monitored, data, units, charts, ctx, footer) { 160 termui.Body.Align() 161 } 162 termui.Render(termui.Body) 163 } 164 }() 165 termui.Loop() 166 return nil 167 } 168 169 //RetrieveMetrics联系附加的geth节点并检索整个集合 170 //收集的系统度量。 171 func retrieveMetrics(client *rpc.Client) (map[string]interface{}, error) { 172 var metrics map[string]interface{} 173 err := client.Call(&metrics, "debug_metrics", true) 174 return metrics, err 175 } 176 177 //ResolveMetrics获取输入度量模式的列表,并将每个模式解析为一个 178 //或更规范的度量名称。 179 func resolveMetrics(metrics map[string]interface{}, patterns []string) []string { 180 res := []string{} 181 for _, pattern := range patterns { 182 res = append(res, resolveMetric(metrics, pattern, "")...) 183 } 184 return res 185 } 186 187 //ResolveMetrics接受一个输入度量模式,并将其解析为一个 188 //或更规范的度量名称。 189 func resolveMetric(metrics map[string]interface{}, pattern string, path string) []string { 190 results := []string{} 191 192 //如果请求嵌套度量,则可以选择性地重复分支(通过逗号) 193 parts := strings.SplitN(pattern, "/", 2) 194 if len(parts) > 1 { 195 for _, variation := range strings.Split(parts[0], ",") { 196 submetrics, ok := metrics[variation].(map[string]interface{}) 197 if !ok { 198 utils.Fatalf("Failed to retrieve system metrics: %s", path+variation) 199 return nil 200 } 201 results = append(results, resolveMetric(submetrics, parts[1], path+variation+"/")...) 202 } 203 return results 204 } 205 //根据最后一个链接是什么,返回或展开 206 for _, variation := range strings.Split(pattern, ",") { 207 switch metric := metrics[variation].(type) { 208 case float64: 209 //找到最终度量值,返回为singleton 210 results = append(results, path+variation) 211 212 case map[string]interface{}: 213 results = append(results, expandMetrics(metric, path+variation+"/")...) 214 215 default: 216 utils.Fatalf("Metric pattern resolved to unexpected type: %v", reflect.TypeOf(metric)) 217 return nil 218 } 219 } 220 return results 221 } 222 223 //ExpandMetrics将整个度量树展开为路径的简单列表。 224 func expandMetrics(metrics map[string]interface{}, path string) []string { 225 //遍历所有字段并单独展开 226 list := []string{} 227 for name, metric := range metrics { 228 switch metric := metric.(type) { 229 case float64: 230 //找到最终度量值,追加到列表 231 list = append(list, path+name) 232 233 case map[string]interface{}: 234 //找到度量树,递归展开 235 list = append(list, expandMetrics(metric, path+name+"/")...) 236 237 default: 238 utils.Fatalf("Metric pattern %s resolved to unexpected type: %v", path+name, reflect.TypeOf(metric)) 239 return nil 240 } 241 } 242 return list 243 } 244 245 //fetchmetric迭代度量图并检索特定的度量图。 246 func fetchMetric(metrics map[string]interface{}, metric string) float64 { 247 parts := strings.Split(metric, "/") 248 for _, part := range parts[:len(parts)-1] { 249 var found bool 250 metrics, found = metrics[part].(map[string]interface{}) 251 if !found { 252 return 0 253 } 254 } 255 if v, ok := metrics[parts[len(parts)-1]].(float64); ok { 256 return v 257 } 258 return 0 259 } 260 261 //刷新图表检索下一批度量,并插入所有新的 262 //活动数据集和图表中的值 263 func refreshCharts(client *rpc.Client, metrics []string, data [][]float64, units []int, charts []*termui.LineChart, ctx *cli.Context, footer *termui.Par) (realign bool) { 264 values, err := retrieveMetrics(client) 265 for i, metric := range metrics { 266 if len(data) < 512 { 267 data[i] = append([]float64{fetchMetric(values, metric)}, data[i]...) 268 } else { 269 data[i] = append([]float64{fetchMetric(values, metric)}, data[i][:len(data[i])-1]...) 270 } 271 if updateChart(metric, data[i], &units[i], charts[i], err) { 272 realign = true 273 } 274 } 275 updateFooter(ctx, err, footer) 276 return 277 } 278 279 //updateChart将数据集插入折线图中,适当地缩放 280 //不显示奇怪的标签,也相应地更新图表标签。 281 func updateChart(metric string, data []float64, base *int, chart *termui.LineChart, err error) (realign bool) { 282 dataUnits := []string{"", "K", "M", "G", "T", "E"} 283 timeUnits := []string{"ns", "µs", "ms", "s", "ks", "ms"} 284 colors := []termui.Attribute{termui.ColorBlue, termui.ColorCyan, termui.ColorGreen, termui.ColorYellow, termui.ColorRed, termui.ColorRed} 285 286 //只提取实际可见的部分数据 287 if chart.Width*2 < len(data) { 288 data = data[:chart.Width*2] 289 } 290 //查找1K以下的最大值和刻度 291 high := 0.0 292 if len(data) > 0 { 293 high = data[0] 294 for _, value := range data[1:] { 295 high = math.Max(high, value) 296 } 297 } 298 unit, scale := 0, 1.0 299 for high >= 1000 && unit+1 < len(dataUnits) { 300 high, unit, scale = high/1000, unit+1, scale*1000 301 } 302 //如果单位改变,重新创建图表(hack设置最大高度…) 303 if unit != *base { 304 realign, *base, *chart = true, unit, *createChart(chart.Height) 305 } 306 //使用缩放值更新图表的数据点 307 if cap(chart.Data) < len(data) { 308 chart.Data = make([]float64, len(data)) 309 } 310 chart.Data = chart.Data[:len(data)] 311 for i, value := range data { 312 chart.Data[i] = value / scale 313 } 314 //用刻度单位更新图表标签 315 units := dataUnits 316 if strings.Contains(metric, "/Percentiles/") || strings.Contains(metric, "/pauses/") || strings.Contains(metric, "/time/") { 317 units = timeUnits 318 } 319 chart.BorderLabel = metric 320 if len(units[unit]) > 0 { 321 chart.BorderLabel += " [" + units[unit] + "]" 322 } 323 chart.LineColor = colors[unit] | termui.AttrBold 324 if err != nil { 325 chart.LineColor = termui.ColorRed | termui.AttrBold 326 } 327 return 328 } 329 330 //创建带有默认配置的空折线图。 331 func createChart(height int) *termui.LineChart { 332 chart := termui.NewLineChart() 333 if runtime.GOOS == "windows" { 334 chart.Mode = "dot" 335 } 336 chart.DataLabels = []string{""} 337 chart.Height = height 338 chart.AxesColor = termui.ColorWhite 339 chart.PaddingBottom = -2 340 341 chart.BorderLabelFg = chart.BorderFg | termui.AttrBold 342 chart.BorderFg = chart.BorderBg 343 344 return chart 345 } 346 347 //updateFooter根据遇到的任何错误更新页脚内容。 348 func updateFooter(ctx *cli.Context, err error, footer *termui.Par) { 349 //生成基本页脚 350 refresh := time.Duration(ctx.Int(monitorCommandRefreshFlag.Name)) * time.Second 351 footer.Text = fmt.Sprintf("Press Ctrl+C to quit. Refresh interval: %v.", refresh) 352 footer.TextFgColor = termui.ThemeAttr("par.fg") | termui.AttrBold 353 354 //附加任何遇到的错误 355 if err != nil { 356 footer.Text = fmt.Sprintf("Error: %v.", err) 357 footer.TextFgColor = termui.ColorRed | termui.AttrBold 358 } 359 }