001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.hbtop.screen.top; 019 020import edu.umd.cs.findbugs.annotations.Nullable; 021import java.util.ArrayList; 022import java.util.List; 023import java.util.stream.Collectors; 024import org.apache.hadoop.hbase.client.Admin; 025import org.apache.hadoop.hbase.hbtop.Record; 026import org.apache.hadoop.hbase.hbtop.mode.Mode; 027import org.apache.hadoop.hbase.hbtop.screen.AbstractScreenView; 028import org.apache.hadoop.hbase.hbtop.screen.Screen; 029import org.apache.hadoop.hbase.hbtop.screen.ScreenView; 030import org.apache.hadoop.hbase.hbtop.terminal.KeyPress; 031import org.apache.hadoop.hbase.hbtop.terminal.Terminal; 032import org.apache.hadoop.hbase.hbtop.terminal.TerminalPrinter; 033import org.apache.hadoop.hbase.hbtop.terminal.TerminalSize; 034import org.apache.yetus.audience.InterfaceAudience; 035 036 037/** 038 * The screen that provides a dynamic real-time view for the HBase metrics. 039 * 040 * This shows the metric {@link Summary} and the metric {@link Record}s. The summary and the 041 * metrics are updated periodically (3 seconds by default). 042 */ 043@InterfaceAudience.Private 044public class TopScreenView extends AbstractScreenView { 045 046 private static final int SUMMARY_START_ROW = 0; 047 private static final int SUMMARY_ROW_NUM = 7; 048 private static final int MESSAGE_ROW = 7; 049 private static final int RECORD_HEADER_ROW = 8; 050 private static final int RECORD_START_ROW = 9; 051 052 private final TopScreenPresenter topScreenPresenter; 053 private int pageSize; 054 055 public TopScreenView(Screen screen, Terminal terminal, long initialRefreshDelay, Admin admin, 056 Mode initialMode) { 057 super(screen, terminal); 058 this.topScreenPresenter = new TopScreenPresenter(this, initialRefreshDelay, 059 new TopScreenModel(admin, initialMode)); 060 } 061 062 @Override 063 public void init() { 064 topScreenPresenter.init(); 065 long delay = topScreenPresenter.refresh(true); 066 setTimer(delay); 067 } 068 069 @Override 070 public ScreenView handleTimer() { 071 long delay = topScreenPresenter.refresh(false); 072 setTimer(delay); 073 return this; 074 } 075 076 @Nullable 077 @Override 078 public ScreenView handleKeyPress(KeyPress keyPress) { 079 switch (keyPress.getType()) { 080 case Enter: 081 topScreenPresenter.refresh(true); 082 return this; 083 084 case ArrowUp: 085 topScreenPresenter.arrowUp(); 086 return this; 087 088 case ArrowDown: 089 topScreenPresenter.arrowDown(); 090 return this; 091 092 case ArrowLeft: 093 topScreenPresenter.arrowLeft(); 094 return this; 095 096 case ArrowRight: 097 topScreenPresenter.arrowRight(); 098 return this; 099 100 case PageUp: 101 topScreenPresenter.pageUp(); 102 return this; 103 104 case PageDown: 105 topScreenPresenter.pageDown(); 106 return this; 107 108 case Home: 109 topScreenPresenter.home(); 110 return this; 111 112 case End: 113 topScreenPresenter.end(); 114 return this; 115 116 case Escape: 117 return null; 118 119 default: 120 // Do nothing 121 break; 122 } 123 124 if (keyPress.getType() != KeyPress.Type.Character) { 125 return unknownCommandMessage(); 126 } 127 128 assert keyPress.getCharacter() != null; 129 switch (keyPress.getCharacter()) { 130 case 'R': 131 topScreenPresenter.switchSortOrder(); 132 break; 133 134 case 'f': 135 cancelTimer(); 136 return topScreenPresenter.transitionToFieldScreen(getScreen(), getTerminal()); 137 138 case 'm': 139 cancelTimer(); 140 return topScreenPresenter.transitionToModeScreen(getScreen(), getTerminal()); 141 142 case 'h': 143 cancelTimer(); 144 return topScreenPresenter.transitionToHelpScreen(getScreen(), getTerminal()); 145 146 case 'd': 147 cancelTimer(); 148 return topScreenPresenter.goToInputModeForRefreshDelay(getScreen(), getTerminal(), 149 MESSAGE_ROW); 150 151 case 'o': 152 cancelTimer(); 153 if (keyPress.isCtrl()) { 154 return topScreenPresenter.goToFilterDisplayMode(getScreen(), getTerminal(), MESSAGE_ROW); 155 } 156 return topScreenPresenter.goToInputModeForFilter(getScreen(), getTerminal(), MESSAGE_ROW, 157 true); 158 159 case 'O': 160 cancelTimer(); 161 return topScreenPresenter.goToInputModeForFilter(getScreen(), getTerminal(), MESSAGE_ROW, 162 false); 163 164 case '=': 165 topScreenPresenter.clearFilters(); 166 break; 167 168 case 'X': 169 topScreenPresenter.adjustFieldLength(); 170 break; 171 172 case 'i': 173 topScreenPresenter.drillDown(); 174 break; 175 176 case 'q': 177 return null; 178 179 default: 180 return unknownCommandMessage(); 181 } 182 return this; 183 } 184 185 @Override 186 public TerminalSize getTerminalSize() { 187 TerminalSize terminalSize = super.getTerminalSize(); 188 updatePageSize(terminalSize); 189 return terminalSize; 190 } 191 192 @Override 193 public TerminalSize doResizeIfNecessary() { 194 TerminalSize terminalSize = super.doResizeIfNecessary(); 195 if (terminalSize == null) { 196 return null; 197 } 198 updatePageSize(terminalSize); 199 return terminalSize; 200 } 201 202 private void updatePageSize(TerminalSize terminalSize) { 203 pageSize = terminalSize.getRows() - SUMMARY_ROW_NUM - 2; 204 if (pageSize < 0) { 205 pageSize = 0; 206 } 207 } 208 209 public int getPageSize() { 210 return pageSize; 211 } 212 213 public void showTopScreen(Summary summary, List<Header> headers, List<Record> records, 214 Record selectedRecord) { 215 showSummary(summary); 216 clearMessage(); 217 showHeaders(headers); 218 showRecords(headers, records, selectedRecord); 219 } 220 221 private void showSummary(Summary summary) { 222 TerminalPrinter printer = getTerminalPrinter(SUMMARY_START_ROW); 223 printer.print(String.format("HBase hbtop - %s", summary.getCurrentTime())).endOfLine(); 224 printer.print(String.format("Version: %s", summary.getVersion())).endOfLine(); 225 printer.print(String.format("Cluster ID: %s", summary.getClusterId())).endOfLine(); 226 printer.print("RegionServer(s): ") 227 .startBold().print(Integer.toString(summary.getServers())).stopBold() 228 .print(" total, ") 229 .startBold().print(Integer.toString(summary.getLiveServers())).stopBold() 230 .print(" live, ") 231 .startBold().print(Integer.toString(summary.getDeadServers())).stopBold() 232 .print(" dead").endOfLine(); 233 printer.print("RegionCount: ") 234 .startBold().print(Integer.toString(summary.getRegionCount())).stopBold() 235 .print(" total, ") 236 .startBold().print(Integer.toString(summary.getRitCount())).stopBold() 237 .print(" rit").endOfLine(); 238 printer.print("Average Cluster Load: ") 239 .startBold().print(String.format("%.2f", summary.getAverageLoad())).stopBold().endOfLine(); 240 printer.print("Aggregate Request/s: ") 241 .startBold().print(Long.toString(summary.getAggregateRequestPerSecond())).stopBold() 242 .endOfLine(); 243 } 244 245 private void showRecords(List<Header> headers, List<Record> records, Record selectedRecord) { 246 TerminalPrinter printer = getTerminalPrinter(RECORD_START_ROW); 247 List<String> buf = new ArrayList<>(headers.size()); 248 for (int i = 0; i < pageSize; i++) { 249 if(i < records.size()) { 250 Record record = records.get(i); 251 buf.clear(); 252 for (Header header : headers) { 253 String value = ""; 254 if (record.containsKey(header.getField())) { 255 value = record.get(header.getField()).asString(); 256 } 257 258 buf.add(limitLineLength(String.format(header.format(), value), header.getLength())); 259 } 260 261 String recordString = String.join(" ", buf); 262 if (!recordString.isEmpty()) { 263 recordString += " "; 264 } 265 266 if (record == selectedRecord) { 267 printer.startHighlight().print(recordString).stopHighlight().endOfLine(); 268 } else { 269 printer.print(recordString).endOfLine(); 270 } 271 } else { 272 printer.endOfLine(); 273 } 274 } 275 } 276 277 private void showHeaders(List<Header> headers) { 278 String header = headers.stream() 279 .map(h -> String.format(h.format(), h.getField().getHeader())) 280 .collect(Collectors.joining(" ")); 281 282 if (!header.isEmpty()) { 283 header += " "; 284 } 285 286 getTerminalPrinter(RECORD_HEADER_ROW).startHighlight().print(header).stopHighlight() 287 .endOfLine(); 288 } 289 290 private String limitLineLength(String line, int length) { 291 if (line.length() > length) { 292 return line.substring(0, length - 1) + "+"; 293 } 294 return line; 295 } 296 297 private void clearMessage() { 298 getTerminalPrinter(MESSAGE_ROW).print("").endOfLine(); 299 } 300 301 private ScreenView unknownCommandMessage() { 302 cancelTimer(); 303 return topScreenPresenter.goToMessageMode(getScreen(), getTerminal(), MESSAGE_ROW, 304 "Unknown command - try 'h' for help"); 305 } 306}