func (this *TopBroker) drawDashboard() { termui.Init() width := termui.TermWidth() height := termui.TermHeight() termui.Close() maxWidth := width - 23 var totalMaxQps, totalMaxBrokerQps float64 for { time.Sleep(this.interval) this.startAll() this.collectAll() datas, maxQps, totalQps := this.showAndResetCounters() if maxQps < 1 { // draw empty lines for _, data := range datas { this.Ui.Output(fmt.Sprintf("%20s", data.host)) } continue } if maxQps > totalMaxBrokerQps { totalMaxBrokerQps = maxQps } if totalQps > totalMaxQps { totalMaxQps = totalQps } refreshScreen() for idx, data := range datas { if idx >= height-2 { break } if data.qps < 0 { panic("negative qps") } this.renderQpsRow(data.host, data.qps, maxQps, maxWidth) } this.Ui.Output(fmt.Sprintf("%20s brokers:%d total:%s cum max[broker:%.1f total:%.1f]", "-SUMMARY-", len(datas), color.Green("%.1f", totalQps), totalMaxBrokerQps, totalMaxQps)) } }
func (this *Top) Run(args []string) (exitCode int) { cmdFlags := flag.NewFlagSet("top", flag.ContinueOnError) cmdFlags.Usage = func() { this.Ui.Output(this.Help()) } cmdFlags.StringVar(&this.zone, "z", ctx.ZkDefaultZone(), "") cmdFlags.StringVar(&this.topicPattern, "t", "", "") cmdFlags.DurationVar(&this.topInterval, "i", time.Second*5, "refresh interval") cmdFlags.StringVar(&this.clusterPattern, "c", "", "") cmdFlags.IntVar(&this.limit, "n", 0, "") cmdFlags.BoolVar(&this.skipIpPrefix, "shortip", true, "") cmdFlags.StringVar(&this.who, "who", "producer", "") cmdFlags.BoolVar(&this.dashboardGraph, "d", false, "") cmdFlags.DurationVar(&this.duration, "for", time.Hour, "") cmdFlags.BoolVar(&this.longFmt, "l", false, "") cmdFlags.BoolVar(&this.batchMode, "b", false, "") if err := cmdFlags.Parse(args); err != nil { return 1 } if this.limit == 0 { termui.Init() this.limit = termui.TermHeight() - 6 // 5=header+footer+cursor line termui.Close() } if this.dashboardGraph { if this.topInterval.Seconds() < 20 { this.topInterval = 20 * time.Second } this.who = "both" go this.clusterOffsetSummary() } if this.who == "c" || this.who == "consumer" { if this.topInterval.Seconds() < 20 { this.topInterval = 20 * time.Second // consumer groups only refresh offset per minute } } this.brokers = make(map[string][]string) this.counters = make(map[string]float64) this.lastCounters = make(map[string]float64) this.partitions = make(map[string]int) this.consumerCounters = make(map[string]float64) this.totalMps = make([]float64, 0, 1000) this.totalConsumerMps = make([]float64, 0, 1000) zkzone := zk.NewZkZone(zk.DefaultConfig(this.zone, ctx.ZoneZkAddrs(this.zone))) zkzone.ForSortedClusters(func(zkcluster *zk.ZkCluster) { if !patternMatched(zkcluster.Name(), this.clusterPattern) { return } switch this.who { case "p", "producer": go this.clusterTopProducers(zkcluster) case "c", "consumer": go this.clusterTopConsumers(zkcluster) case "both": go this.clusterTopConsumers(zkcluster) go this.clusterTopProducers(zkcluster) default: this.Ui.Error(fmt.Sprintf("unknown type: %s", this.who)) } }) if this.dashboardGraph { this.drawDashboard() return } ticker := time.NewTicker(this.topInterval) defer ticker.Stop() keyboardPressed := make(chan struct{}) go func() { var b []byte = make([]byte, 1) for { os.Stdin.Read(b) keyboardPressed <- struct{}{} } }() startAt := time.Now() for { select { case <-keyboardPressed: case <-ticker.C: } if time.Since(startAt) >= this.duration { break } if this.batchMode { this.Ui.Output(bjtime.TimeToString(bjtime.NowBj())) } else { refreshScreen() } // header if this.longFmt { this.Ui.Output(fmt.Sprintf("%-9s %15s %-30s %42s %20s %15s", this.who, "cluster", "brokers", "topic", "cum num", "mps")) // mps=msg per second this.Ui.Output(fmt.Sprintf(strings.Repeat("-", 136))) } else { this.Ui.Output(fmt.Sprintf("%-9s %20s %50s %20s %15s", this.who, "cluster", "topic", "cum num", "mps")) // mps=msg per second this.Ui.Output(fmt.Sprintf(strings.Repeat("-", 118))) } this.showAndResetCounters() } return }
func (this *Top) drawDashboard() { err := termui.Init() swallow(err) defer termui.Close() termui.UseTheme("helloworld") maxRound := termui.TermWidth() / 2 refreshProducerData := func(round int) []float64 { this.showAndResetCounters() if round%maxRound == (maxRound - 5) { this.mu.Lock() tailLen := len(this.totalMps) - 2 tail := this.totalMps[tailLen:] this.totalMps = make([]float64, 2, 1000) copy(this.totalMps, tail) this.mu.Unlock() } return this.totalMps } refreshConsumerData := func(round int) []float64 { if round%maxRound == (maxRound - 5) { this.mu.Lock() tailLen := len(this.totalConsumerMps) - 2 tail := this.totalConsumerMps[tailLen:] this.totalConsumerMps = make([]float64, 2, 1000) copy(this.totalConsumerMps, tail) this.mu.Unlock() } return this.totalConsumerMps } producerChart := termui.NewLineChart() producerChart.Mode = "dot" producerChart.Border.Label = fmt.Sprintf("producer mps totals: %s %s %s", this.zone, this.clusterPattern, this.topicPattern) producerChart.Data = refreshProducerData(0) producerChart.Width = termui.TermWidth() / 2 producerChart.Height = termui.TermHeight() producerChart.X = 0 producerChart.Y = 0 producerChart.AxesColor = termui.ColorWhite producerChart.LineColor = termui.ColorGreen | termui.AttrBold consumerChart := termui.NewLineChart() consumerChart.Mode = "dot" consumerChart.Border.Label = fmt.Sprintf("consumer mps totals: %s %s %s", this.zone, this.clusterPattern, this.topicPattern) consumerChart.Data = refreshConsumerData(0) consumerChart.Width = termui.TermWidth() / 2 consumerChart.Height = termui.TermHeight() consumerChart.X = termui.TermWidth() / 2 consumerChart.Y = 0 consumerChart.AxesColor = termui.ColorWhite consumerChart.LineColor = termui.ColorRed | termui.AttrBold evt := make(chan termbox.Event) go func() { for { evt <- termbox.PollEvent() } }() termui.Render(producerChart, consumerChart) tick := time.NewTicker(this.topInterval) defer tick.Stop() rounds := 0 for { select { case e := <-evt: if e.Type == termbox.EventKey && e.Ch == 'q' { return } case <-tick.C: // refresh data, and skip the first 2 rounds rounds++ if rounds > 1 { producerChart.Data = refreshProducerData(rounds) consumerChart.Data = refreshConsumerData(rounds) termui.Render(producerChart, consumerChart) } } } }