func (this *Brokers) Run(args []string) (exitCode int) { var ( zone string debug bool ) cmdFlags := flag.NewFlagSet("brokers", flag.ContinueOnError) cmdFlags.Usage = func() { this.Ui.Output(this.Help()) } cmdFlags.StringVar(&zone, "z", ctx.ZkDefaultZone(), "") cmdFlags.StringVar(&this.cluster, "c", "", "") cmdFlags.BoolVar(&debug, "debug", false, "") cmdFlags.BoolVar(&this.ipInNumber, "n", false, "") cmdFlags.BoolVar(&this.staleOnly, "stale", false, "") cmdFlags.BoolVar(&this.showVersions, "versions", false, "") if err := cmdFlags.Parse(args); err != nil { return 1 } if this.showVersions { this.doShowVersions() return } if debug { sarama.Logger = log.New(os.Stderr, color.Magenta("[sarama]"), log.LstdFlags) } if zone != "" { ensureZoneValid(zone) zkzone := zk.NewZkZone(zk.DefaultConfig(zone, ctx.ZoneZkAddrs(zone))) this.displayZoneBrokers(zkzone) return } // print all brokers on all zones by default forSortedZones(func(zkzone *zk.ZkZone) { this.displayZoneBrokers(zkzone) }) return }
func main() { defer func() { if err := recover(); err != nil { fmt.Println(err) debug.PrintStack() } }() if gateway.Options.Debug { log.SetFlags(log.LstdFlags | log.Llongfile) // TODO zk sdk uses this log.SetPrefix(color.Magenta("[log]")) } else { log.SetOutput(ioutil.Discard) } if gateway.Options.KillFile != "" { if err := signal.SignalProcessByPidFile(gateway.Options.KillFile, syscall.SIGUSR2); err != nil { panic(err) } fmt.Println("kateway killed") os.Exit(0) } if gateway.Options.GolangTrace { // go tool trace kateway xxx.pprof f, err := os.Create(time.Now().Format("2006-01-02T150405.pprof")) if err != nil { panic(err) } defer f.Close() if err = trace.Start(f); err != nil { panic(err) } defer trace.Stop() } fmt.Fprintln(os.Stderr, strings.TrimSpace(logo)) if gateway.Options.PidFile != "" { pid := os.Getpid() if err := ioutil.WriteFile(gateway.Options.PidFile, []byte(fmt.Sprintf("%d", pid)), 0644); err != nil { panic(err) } } gateway.SetupLogging(gateway.Options.LogFile, gateway.Options.LogLevel, gateway.Options.CrashLogFile) // load config if gateway.Options.ConfigFile == "" { ctx.LoadFromHome() } else { _, err := os.Stat(gateway.Options.ConfigFile) if err != nil { panic(err) } ctx.LoadConfig(gateway.Options.ConfigFile) } gw := gateway.New(gateway.Options.Id) if err := gw.Start(); err != nil { glog.Error(err.Error()) glog.Info("kateway[%s@%s] bye!", gafka.BuildId, gafka.BuiltAt) glog.Close() os.Exit(1) } t0 := time.Now() gw.ServeForever() glog.Info("kateway[%s@%s] %s, bye!", gafka.BuildId, gafka.BuiltAt, time.Since(t0)) glog.Close() if gateway.Options.PidFile != "" { syscall.Unlink(gateway.Options.PidFile) } }
func (this *Lags) printConsumersLag(zkcluster *zk.ZkCluster) { // sort by group name consumersByGroup := zkcluster.ConsumersByGroup(this.groupPattern) sortedGroups := make([]string, 0, len(consumersByGroup)) for group, _ := range consumersByGroup { sortedGroups = append(sortedGroups, group) } sort.Strings(sortedGroups) for _, group := range sortedGroups { lines := make([]string, 0, 100) sortedTopicAndPartitionIds := make([]string, 0) consumers := make(map[string]zk.ConsumerMeta) for _, t := range consumersByGroup[group] { key := fmt.Sprintf("%s:%s", t.Topic, t.PartitionId) sortedTopicAndPartitionIds = append(sortedTopicAndPartitionIds, key) consumers[key] = t } sort.Strings(sortedTopicAndPartitionIds) for _, topicAndPartitionId := range sortedTopicAndPartitionIds { consumer := consumers[topicAndPartitionId] if !patternMatched(consumer.Topic, this.topicPattern) { continue } var ( lagOutput string symbol string ) if consumer.Lag > int64(this.lagThreshold) { lagOutput = color.Red("%15s", gofmt.Comma(consumer.Lag)) if consumer.Online { symbol = color.Yellow("⚠︎︎") } else { symbol = color.Yellow("◎") } } else { lagOutput = color.Blue("%15s", gofmt.Comma(consumer.Lag)) if consumer.Online { symbol = color.Green("◉") } else { symbol = color.Yellow("◎") } } if consumer.Online { if this.problematicMode && consumer.Lag <= int64(this.lagThreshold) { continue } var ( host string uptime string ) if consumer.ConsumerZnode == nil { host = "unrecognized" uptime = "-" } else { host = color.Green("%s", consumer.ConsumerZnode.Host()) if time.Since(consumer.ConsumerZnode.Uptime()) < time.Hour { uptime = color.Magenta(gofmt.PrettySince(consumer.ConsumerZnode.Uptime())) } else { uptime = gofmt.PrettySince(consumer.ConsumerZnode.Uptime()) } } lines = append(lines, fmt.Sprintf("\t%s %35s/%-2s %12s -> %-15s %s %-10s %s %s", symbol, consumer.Topic, consumer.PartitionId, gofmt.Comma(consumer.ProducerOffset), gofmt.Comma(consumer.ConsumerOffset), lagOutput, gofmt.PrettySince(consumer.Mtime.Time()), host, uptime)) } else if !this.onlineOnly { lines = append(lines, fmt.Sprintf("\t%s %35s/%-2s %12s -> %-12s %s %s", symbol, consumer.Topic, consumer.PartitionId, gofmt.Comma(consumer.ProducerOffset), gofmt.Comma(consumer.ConsumerOffset), lagOutput, gofmt.PrettySince(consumer.Mtime.Time()))) } } if len(lines) > 0 { this.Ui.Output(strings.Repeat(" ", 4) + group) for _, l := range lines { this.Ui.Output(l) } } } }
func (this *UnderReplicated) Run(args []string) (exitCode int) { cmdFlags := flag.NewFlagSet("underreplicated", flag.ContinueOnError) cmdFlags.Usage = func() { this.Ui.Output(this.Help()) } cmdFlags.StringVar(&this.zone, "z", ctx.ZkDefaultZone(), "") cmdFlags.StringVar(&this.cluster, "c", "", "") cmdFlags.BoolVar(&this.debug, "debug", false, "") if err := cmdFlags.Parse(args); err != nil { return 1 } if this.debug { sarama.Logger = log.New(os.Stderr, color.Magenta("[sarama]"), log.LstdFlags) } if this.zone == "" { forSortedZones(func(zkzone *zk.ZkZone) { this.Ui.Output(zkzone.Name()) zkzone.ForSortedClusters(func(zkcluster *zk.ZkCluster) { lines := this.displayUnderReplicatedPartitionsOfCluster(zkcluster) if len(lines) > 0 { this.Ui.Output(fmt.Sprintf("%s %s", strings.Repeat(" ", 4), zkcluster.Name())) for _, l := range lines { this.Ui.Warn(l) } } }) printSwallowedErrors(this.Ui, zkzone) }) return } // a single zone ensureZoneValid(this.zone) zkzone := zk.NewZkZone(zk.DefaultConfig(this.zone, ctx.ZoneZkAddrs(this.zone))) this.Ui.Output(zkzone.Name()) if this.cluster != "" { zkcluster := zkzone.NewCluster(this.cluster) lines := this.displayUnderReplicatedPartitionsOfCluster(zkcluster) if len(lines) > 0 { this.Ui.Output(fmt.Sprintf("%s %s", strings.Repeat(" ", 4), zkcluster.Name())) for _, l := range lines { this.Ui.Warn(l) } } } else { // all clusters zkzone.ForSortedClusters(func(zkcluster *zk.ZkCluster) { lines := this.displayUnderReplicatedPartitionsOfCluster(zkcluster) if len(lines) > 0 { this.Ui.Output(fmt.Sprintf("%s %s", strings.Repeat(" ", 4), zkcluster.Name())) for _, l := range lines { this.Ui.Warn(l) } } }) } printSwallowedErrors(this.Ui, zkzone) return }
func (this *Topology) displayZoneTopology(zkzone *zk.ZkZone) { this.Ui.Output(zkzone.Name()) // {cluster: {topic: brokerHostInfo}} brokerInstances := make(map[string]map[string]*brokerHostInfo) zkzone.ForSortedBrokers(func(cluster string, liveBrokers map[string]*zk.BrokerZnode) { if len(liveBrokers) == 0 { this.Ui.Warn(fmt.Sprintf("empty brokers in cluster[%s]", cluster)) return } if this.cluster != "" && this.cluster != cluster { return } brokerInstances[cluster] = make(map[string]*brokerHostInfo) for _, broker := range liveBrokers { if !patternMatched(broker.Host, this.hostPattern) { continue } if _, present := brokerInstances[cluster][broker.Host]; !present { brokerInstances[cluster][broker.Host] = newBrokerHostInfo() } brokerInstances[cluster][broker.Host].addPort(broker.Port, broker.Uptime()) } // find how many partitions a broker is leading zkcluster := zkzone.NewCluster(cluster) brokerList := zkcluster.BrokerList() if len(brokerList) == 0 { this.Ui.Warn(fmt.Sprintf("empty brokers in cluster[%s]", cluster)) return } kfk, err := sarama.NewClient(brokerList, sarama.NewConfig()) if err != nil { this.Ui.Error(color.Red(" %+v %s", brokerList, err.Error())) return } topics, err := kfk.Topics() swallow(err) for _, topic := range topics { partions, err := kfk.WritablePartitions(topic) swallow(err) for _, partitionID := range partions { leader, err := kfk.Leader(topic, partitionID) swallow(err) host, _, err := net.SplitHostPort(leader.Addr()) swallow(err) if !patternMatched(host, this.hostPattern) { continue } latestOffset, err := kfk.GetOffset(topic, partitionID, sarama.OffsetNewest) if err != nil { this.Ui.Error(fmt.Sprintf("%s %s %v", cluster, topic, err)) continue } oldestOffset, err := kfk.GetOffset(topic, partitionID, sarama.OffsetOldest) if err != nil { this.Ui.Error(fmt.Sprintf("%s %s %v", cluster, topic, err)) continue } brokerInstances[cluster][host].topicMsgs[topic] += (latestOffset - oldestOffset) brokerInstances[cluster][host].addTopicPartition(topic, partitionID) } } }) hosts := make(map[string]struct{}) zkzone.ForSortedClusters(func(zkcluster *zk.ZkCluster) { for host, _ := range brokerInstances[zkcluster.Name()] { hosts[host] = struct{}{} } }) sortedHosts := make([]string, 0) for host, _ := range hosts { sortedHosts = append(sortedHosts, host) } sort.Strings(sortedHosts) // sort by host ip sortedClusters := make([]string, 0, len(brokerInstances)) for c, _ := range brokerInstances { sortedClusters = append(sortedClusters, c) } sort.Strings(sortedClusters) portN := 0 hostN := 0 topicN := 0 partitionN := 0 for _, host := range sortedHosts { tn := 0 pn := 0 mn := int64(0) ports := make([]int, 0) for _, cluster := range sortedClusters { if _, present := brokerInstances[cluster][host]; !present { continue } tn += len(brokerInstances[cluster][host].topicPartitions) pn += brokerInstances[cluster][host].leadingPartitions() mn += brokerInstances[cluster][host].totalMsgsInStock() ports = append(ports, brokerInstances[cluster][host].tcpPorts...) } portN += len(ports) topicN += tn partitionN += pn hostN += 1 this.Ui.Output(fmt.Sprintf(" %s leading: %2dT %3dP %15sM ports %2d:%+v", color.Green("%15s", host), tn, pn, gofmt.Comma(mn), len(ports), ports)) if this.verbose { for _, cluster := range sortedClusters { if _, present := brokerInstances[cluster][host]; !present { continue } for _, tcpPort := range brokerInstances[cluster][host].tcpPorts { this.Ui.Output(fmt.Sprintf("%40d %s", tcpPort, gofmt.PrettySince(brokerInstances[cluster][host].uptimes[tcpPort]))) } } for _, cluster := range sortedClusters { if _, present := brokerInstances[cluster][host]; !present { continue } this.Ui.Output(color.Magenta("%30s", cluster)) for topic, partitions := range brokerInstances[cluster][host].topicPartitions { this.Ui.Output(fmt.Sprintf("%40s: %15sM P%2d %+v", topic, gofmt.Comma(brokerInstances[cluster][host].topicMsgs[topic]), len(partitions), partitions)) } } } } this.Ui.Output(fmt.Sprintf("%17s host:%d, topic:%d, partition:%d, instance:%d", "-TOTAL-", hostN, topicN, partitionN, portN)) }
func (this *Get) Run(args []string) (exitCode int) { cmdFlags := flag.NewFlagSet("get", flag.ContinueOnError) cmdFlags.Usage = func() { this.Ui.Output(this.Help()) } cmdFlags.StringVar(&this.zone, "z", ctx.ZkDefaultZone(), "") cmdFlags.BoolVar(&this.verbose, "l", false, "") cmdFlags.BoolVar(&this.recursive, "R", false, "") cmdFlags.StringVar(&this.likePattern, "like", "", "") if err := cmdFlags.Parse(args); err != nil { return 1 } if this.zone == "" { this.Ui.Error("unknown zone") return 2 } if len(args) == 0 { this.Ui.Error("missing path") return 2 } this.path = args[len(args)-1] zkzone := gzk.NewZkZone(gzk.DefaultConfig(this.zone, ctx.ZoneZkAddrs(this.zone))) defer zkzone.Close() if this.recursive { data, stat, err := zkzone.Conn().Get(this.path) must(err) if stat.EphemeralOwner > 0 { this.Ui.Output(color.Yellow(this.path)) } else { this.Ui.Output(color.Green(this.path)) } if len(data) > 0 { if this.verbose { this.Ui.Output(fmt.Sprintf("%s %#v", strings.Repeat(" ", 3), stat)) this.Ui.Output(fmt.Sprintf("%s %v", strings.Repeat(" ", 3), data)) } this.Ui.Output(fmt.Sprintf("%s %s", strings.Repeat(" ", 3), string(data))) } this.showChildrenRecursively(zkzone.Conn(), this.path) return 0 } conn := zkzone.Conn() data, stat, err := conn.Get(this.path) must(err) if len(data) == 0 { this.Ui.Output("empty znode") return } if this.verbose { this.Ui.Output(color.Magenta("%#v", *stat)) } this.Ui.Output(color.Green("Data Bytes")) fmt.Println(data) this.Ui.Output(color.Green("Data as String")) this.Ui.Output(string(data)) return }
func (this *Topics) Run(args []string) (exitCode int) { var ( zone string cluster string addTopic string delTopic string killTopic, restoreTopic string replicas int partitions int retentionInMinute int resetConf bool debug bool summaryMode bool configged bool ) cmdFlags := flag.NewFlagSet("brokers", flag.ContinueOnError) cmdFlags.Usage = func() { this.Ui.Output(this.Help()) } cmdFlags.StringVar(&zone, "z", ctx.ZkDefaultZone(), "") cmdFlags.StringVar(&this.topicPattern, "t", "", "") cmdFlags.StringVar(&cluster, "c", "", "") cmdFlags.BoolVar(&this.verbose, "l", false, "") cmdFlags.BoolVar(&this.ipInNumber, "n", false, "") cmdFlags.StringVar(&addTopic, "add", "", "") cmdFlags.BoolVar(&summaryMode, "sum", false, "") cmdFlags.StringVar(&killTopic, "kill", "", "") cmdFlags.StringVar(&restoreTopic, "restore", "", "") cmdFlags.BoolVar(&this.plainMode, "plain", false, "") cmdFlags.StringVar(&delTopic, "del", "", "") cmdFlags.IntVar(&partitions, "partitions", 1, "") cmdFlags.DurationVar(&this.since, "since", 0, "") cmdFlags.BoolVar(&configged, "cf", false, "") cmdFlags.BoolVar(&debug, "debug", false, "") cmdFlags.BoolVar(&resetConf, "cfreset", false, "") cmdFlags.Int64Var(&this.count, "count", 0, "") cmdFlags.IntVar(&retentionInMinute, "retention", -1, "") cmdFlags.IntVar(&replicas, "replicas", 2, "") if err := cmdFlags.Parse(args); err != nil { return 1 } if validateArgs(this, this.Ui). on("-add", "-c"). on("-del", "-c"). on("-retention", "-c", "-t"). on("-cfreset", "-c", "-t"). requireAdminRights("-add", "-del", "-retention"). invalid(args) { return 2 } if this.count > 0 { this.verbose = true } if debug { sarama.Logger = log.New(os.Stderr, color.Magenta("[sarama]"), log.LstdFlags) } if addTopic != "" { zkzone := zk.NewZkZone(zk.DefaultConfig(zone, ctx.ZoneZkAddrs(zone))) zkcluster := zkzone.NewCluster(cluster) swallow(this.addTopic(zkcluster, addTopic, replicas, partitions)) return } else if delTopic != "" { zkzone := zk.NewZkZone(zk.DefaultConfig(zone, ctx.ZoneZkAddrs(zone))) zkcluster := zkzone.NewCluster(cluster) swallow(this.delTopic(zkcluster, delTopic)) return } ensureZoneValid(zone) zkzone := zk.NewZkZone(zk.DefaultConfig(zone, ctx.ZoneZkAddrs(zone))) if retentionInMinute > 0 { zkcluster := zkzone.NewCluster(cluster) this.configTopic(zkcluster, this.topicPattern, retentionInMinute) return } if resetConf { zkcluster := zkzone.NewCluster(cluster) this.resetTopicConfig(zkcluster, this.topicPattern) configged = true // after reset, display most recent znode info } if configged { // output header this.Ui.Output(fmt.Sprintf("%25s %-40s %15s", "cluster", "topic", "mtime")) this.Ui.Output(fmt.Sprintf("%25s %40s %15s", strings.Repeat("-", 25), strings.Repeat("-", 40), strings.Repeat("-", 15))) displayTopicConfigs := func(zkcluster *zk.ZkCluster) { // sort by topic name configs := zkcluster.ConfiggedTopics() sortedTopics := make([]string, 0, len(configs)) for topic, _ := range configs { sortedTopics = append(sortedTopics, topic) } sort.Strings(sortedTopics) for _, topic := range sortedTopics { if !patternMatched(topic, this.topicPattern) { continue } configInfo := configs[topic] this.Ui.Output(fmt.Sprintf("%25s %40s %15s %s", zkcluster.Name(), topic, gofmt.PrettySince(configInfo.Mtime), configInfo.Config)) } } if cluster == "" { zkzone.ForSortedClusters(func(zkcluster *zk.ZkCluster) { displayTopicConfigs(zkcluster) }) } else { zkcluster := zkzone.NewCluster(cluster) displayTopicConfigs(zkcluster) } return } if summaryMode { this.printSummary(zkzone, cluster) return } if !this.verbose { // output header this.Ui.Output(fmt.Sprintf("%30s %-50s", "cluster", "topic")) this.Ui.Output(fmt.Sprintf("%30s %50s", strings.Repeat("-", 30), strings.Repeat("-", 50))) } if cluster != "" { zkcluster := zkzone.NewCluster(cluster) this.displayTopicsOfCluster(zkcluster) this.Ui.Output(fmt.Sprintf("%25s %d", "-TOTAL Topics-", this.topicN)) this.Ui.Output(fmt.Sprintf("%25s %d", "-TOTAL Partitions-", this.partitionN)) if this.verbose { if this.plainMode { fmt.Printf("%25s %s\n", "-FLAT Messages-", gofmt.Comma(this.totalMsgs)) fmt.Printf("%25s %s\n", "-CUM Messages-", gofmt.Comma(this.totalOffsets)) } else { this.Ui.Output(fmt.Sprintf("%25s %s", "-FLAT Messages-", gofmt.Comma(this.totalMsgs))) this.Ui.Output(fmt.Sprintf("%25s %s", "-CUM Messages-", gofmt.Comma(this.totalOffsets))) } } return } // all clusters zkzone.ForSortedClusters(func(zkcluster *zk.ZkCluster) { this.displayTopicsOfCluster(zkcluster) }) this.Ui.Output(fmt.Sprintf("%25s %d", "-TOTAL Topics-", this.topicN)) this.Ui.Output(fmt.Sprintf("%25s %d", "-TOTAL Partitions-", this.partitionN)) if this.verbose { if this.plainMode { fmt.Printf("%25s %s\n", "-FLAT Messages-", gofmt.Comma(this.totalMsgs)) fmt.Printf("%25s %s\n", "-CUM Messages-", gofmt.Comma(this.totalOffsets)) } else { this.Ui.Output(fmt.Sprintf("%25s %s", "-FLAT Messages-", gofmt.Comma(this.totalMsgs))) this.Ui.Output(fmt.Sprintf("%25s %s", "-CUM Messages-", gofmt.Comma(this.totalOffsets))) } } return }
func (this *Topics) displayTopicsOfCluster(zkcluster *zk.ZkCluster) { echoBuffer := func(lines []string) { for _, l := range lines { this.Ui.Output(l) } } linesInTopicMode := make([]string, 0) if this.verbose { linesInTopicMode = this.echoOrBuffer(zkcluster.Name(), linesInTopicMode) } // get all alive brokers within this cluster brokers := zkcluster.Brokers() if len(brokers) == 0 { linesInTopicMode = this.echoOrBuffer(fmt.Sprintf("%4s%s", " ", color.Red("%s empty brokers", zkcluster.Name())), linesInTopicMode) echoBuffer(linesInTopicMode) return } if this.verbose { sortedBrokerIds := make([]string, 0, len(brokers)) for brokerId, _ := range brokers { sortedBrokerIds = append(sortedBrokerIds, brokerId) } sort.Strings(sortedBrokerIds) for _, brokerId := range sortedBrokerIds { if this.ipInNumber { linesInTopicMode = this.echoOrBuffer(fmt.Sprintf("%4s%s %s", " ", color.Green(brokerId), brokers[brokerId]), linesInTopicMode) } else { linesInTopicMode = this.echoOrBuffer(fmt.Sprintf("%4s%s %s", " ", color.Green(brokerId), brokers[brokerId].NamedString()), linesInTopicMode) } } } kfk, err := sarama.NewClient(zkcluster.BrokerList(), saramaConfig()) if err != nil { if this.verbose { linesInTopicMode = this.echoOrBuffer(color.Yellow("%5s%+v %s", " ", zkcluster.BrokerList(), err.Error()), linesInTopicMode) } return } defer kfk.Close() topics, err := kfk.Topics() swallow(err) if len(topics) == 0 { if this.topicPattern == "" && this.verbose { linesInTopicMode = this.echoOrBuffer(fmt.Sprintf("%5s%s", " ", color.Magenta("no topics")), linesInTopicMode) echoBuffer(linesInTopicMode) } return } sortedTopics := make([]string, 0, len(topics)) for _, t := range topics { sortedTopics = append(sortedTopics, t) } sort.Strings(sortedTopics) topicsCtime := zkcluster.TopicsCtime() hasTopicMatched := false for _, topic := range sortedTopics { if !patternMatched(topic, this.topicPattern) { continue } if this.since > 0 && time.Since(topicsCtime[topic]) > this.since { continue } this.topicN++ hasTopicMatched = true if this.verbose { linesInTopicMode = this.echoOrBuffer(strings.Repeat(" ", 4)+color.Cyan(topic), linesInTopicMode) } // get partitions and check if some dead alivePartitions, err := kfk.WritablePartitions(topic) swallow(err) partions, err := kfk.Partitions(topic) swallow(err) if len(alivePartitions) != len(partions) { linesInTopicMode = this.echoOrBuffer(fmt.Sprintf("%30s %s %s P: %s/%+v", zkcluster.Name(), color.Cyan("%-50s", topic), color.Red("partial dead"), color.Green("%+v", alivePartitions), partions), linesInTopicMode) } replicas, err := kfk.Replicas(topic, partions[0]) if err != nil { this.Ui.Error(fmt.Sprintf("%s/%d %v", topic, partions[0], err)) } this.partitionN += len(partions) if !this.verbose { linesInTopicMode = this.echoOrBuffer(fmt.Sprintf("%30s %s %3dP %dR %s", zkcluster.Name(), color.Cyan("%-50s", topic), len(partions), len(replicas), gofmt.PrettySince(topicsCtime[topic])), linesInTopicMode) continue } for _, partitionID := range alivePartitions { leader, err := kfk.Leader(topic, partitionID) swallow(err) replicas, err := kfk.Replicas(topic, partitionID) if err != nil { this.Ui.Error(fmt.Sprintf("%s/%d %v", topic, partitionID, err)) } isr, isrMtime, partitionCtime := zkcluster.Isr(topic, partitionID) isrMtimeSince := gofmt.PrettySince(isrMtime) if time.Since(isrMtime).Hours() < 24 { // ever out of sync last 24h isrMtimeSince = color.Magenta(isrMtimeSince) } underReplicated := false if len(isr) != len(replicas) { underReplicated = true } latestOffset, err := kfk.GetOffset(topic, partitionID, sarama.OffsetNewest) swallow(err) oldestOffset, err := kfk.GetOffset(topic, partitionID, sarama.OffsetOldest) swallow(err) if this.count > 0 && (latestOffset-oldestOffset) < this.count { continue } this.totalMsgs += latestOffset - oldestOffset this.totalOffsets += latestOffset if !underReplicated { linesInTopicMode = this.echoOrBuffer(fmt.Sprintf("%8d Leader:%s Replicas:%+v Isr:%+v Offset:%16s - %-16s Num:%-15s %s-%s", partitionID, color.Green("%d", leader.ID()), replicas, isr, gofmt.Comma(oldestOffset), gofmt.Comma(latestOffset), gofmt.Comma(latestOffset-oldestOffset), gofmt.PrettySince(partitionCtime), isrMtimeSince), linesInTopicMode) } else { // use red for alert linesInTopicMode = this.echoOrBuffer(fmt.Sprintf("%8d Leader:%s Replicas:%+v Isr:%s Offset:%16s - %-16s Num:%-15s %s-%s", partitionID, color.Green("%d", leader.ID()), replicas, color.Red("%+v", isr), gofmt.Comma(oldestOffset), gofmt.Comma(latestOffset), gofmt.Comma(latestOffset-oldestOffset), gofmt.PrettySince(partitionCtime), isrMtimeSince), linesInTopicMode) } } } if this.topicPattern != "" { if hasTopicMatched { echoBuffer(linesInTopicMode) } } else { echoBuffer(linesInTopicMode) } }