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.RecordFilter; 027import org.apache.hadoop.hbase.hbtop.field.Field; 028import org.apache.hadoop.hbase.hbtop.mode.Mode; 029import org.apache.hadoop.hbase.hbtop.screen.AbstractScreenView; 030import org.apache.hadoop.hbase.hbtop.screen.Screen; 031import org.apache.hadoop.hbase.hbtop.screen.ScreenView; 032import org.apache.hadoop.hbase.hbtop.terminal.KeyPress; 033import org.apache.hadoop.hbase.hbtop.terminal.Terminal; 034import org.apache.hadoop.hbase.hbtop.terminal.TerminalPrinter; 035import org.apache.hadoop.hbase.hbtop.terminal.TerminalSize; 036import org.apache.yetus.audience.InterfaceAudience; 037 038/** 039 * The screen that provides a dynamic real-time view for the HBase metrics. This shows the metric 040 * {@link Summary} and the metric {@link Record}s. The summary and the metrics are updated 041 * 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 Integer pageSize; 054 055 public TopScreenView(Screen screen, Terminal terminal, long initialRefreshDelay, Admin admin, 056 Mode initialMode, @Nullable List<Field> initialFields, @Nullable Field initialSortField, 057 @Nullable Boolean initialAscendingSort, @Nullable List<RecordFilter> initialFilters, 058 long numberOfIterations) { 059 super(screen, terminal); 060 this.topScreenPresenter = 061 new TopScreenPresenter( 062 this, initialRefreshDelay, new TopScreenModel(admin, initialMode, initialFields, 063 initialSortField, initialAscendingSort, initialFilters), 064 initialFields, numberOfIterations); 065 } 066 067 @Override 068 public void init() { 069 topScreenPresenter.init(); 070 long delay = topScreenPresenter.refresh(true); 071 setTimer(delay); 072 } 073 074 @Nullable 075 @Override 076 public ScreenView handleTimer() { 077 long delay = topScreenPresenter.refresh(false); 078 setTimer(delay); 079 return topScreenPresenter.isIterationFinished() ? null : this; 080 } 081 082 @Nullable 083 @Override 084 public ScreenView handleKeyPress(KeyPress keyPress) { 085 switch (keyPress.getType()) { 086 case Enter: 087 topScreenPresenter.refresh(true); 088 return topScreenPresenter.isIterationFinished() ? null : this; 089 090 case ArrowUp: 091 topScreenPresenter.arrowUp(); 092 return topScreenPresenter.isIterationFinished() ? null : this; 093 094 case ArrowDown: 095 topScreenPresenter.arrowDown(); 096 return topScreenPresenter.isIterationFinished() ? null : this; 097 098 case ArrowLeft: 099 topScreenPresenter.arrowLeft(); 100 return topScreenPresenter.isIterationFinished() ? null : this; 101 102 case ArrowRight: 103 topScreenPresenter.arrowRight(); 104 return topScreenPresenter.isIterationFinished() ? null : this; 105 106 case PageUp: 107 topScreenPresenter.pageUp(); 108 return topScreenPresenter.isIterationFinished() ? null : this; 109 110 case PageDown: 111 topScreenPresenter.pageDown(); 112 return topScreenPresenter.isIterationFinished() ? null : this; 113 114 case Home: 115 topScreenPresenter.home(); 116 return topScreenPresenter.isIterationFinished() ? null : this; 117 118 case End: 119 topScreenPresenter.end(); 120 return topScreenPresenter.isIterationFinished() ? null : this; 121 122 case Escape: 123 return null; 124 125 default: 126 // Do nothing 127 break; 128 } 129 130 if (keyPress.getType() != KeyPress.Type.Character) { 131 return unknownCommandMessage(); 132 } 133 134 assert keyPress.getCharacter() != null; 135 switch (keyPress.getCharacter()) { 136 case 'R': 137 topScreenPresenter.switchSortOrder(); 138 break; 139 140 case 'f': 141 cancelTimer(); 142 return topScreenPresenter.transitionToFieldScreen(getScreen(), getTerminal()); 143 144 case 'm': 145 cancelTimer(); 146 return topScreenPresenter.transitionToModeScreen(getScreen(), getTerminal()); 147 148 case 'h': 149 cancelTimer(); 150 return topScreenPresenter.transitionToHelpScreen(getScreen(), getTerminal()); 151 152 case 'd': 153 cancelTimer(); 154 return topScreenPresenter.goToInputModeForRefreshDelay(getScreen(), getTerminal(), 155 MESSAGE_ROW); 156 157 case 'o': 158 cancelTimer(); 159 if (keyPress.isCtrl()) { 160 return topScreenPresenter.goToFilterDisplayMode(getScreen(), getTerminal(), MESSAGE_ROW); 161 } 162 return topScreenPresenter.goToInputModeForFilter(getScreen(), getTerminal(), MESSAGE_ROW, 163 true); 164 165 case 'O': 166 cancelTimer(); 167 return topScreenPresenter.goToInputModeForFilter(getScreen(), getTerminal(), MESSAGE_ROW, 168 false); 169 170 case '=': 171 topScreenPresenter.clearFilters(); 172 break; 173 174 case 'X': 175 topScreenPresenter.adjustFieldLength(); 176 break; 177 178 case 'i': 179 topScreenPresenter.drillDown(); 180 break; 181 182 case 'q': 183 return null; 184 185 default: 186 return unknownCommandMessage(); 187 } 188 return this; 189 } 190 191 @Nullable 192 @Override 193 public TerminalSize getTerminalSize() { 194 TerminalSize terminalSize = super.getTerminalSize(); 195 if (terminalSize == null) { 196 return null; 197 } 198 updatePageSize(terminalSize); 199 return terminalSize; 200 } 201 202 @Nullable 203 @Override 204 public TerminalSize doResizeIfNecessary() { 205 TerminalSize terminalSize = super.doResizeIfNecessary(); 206 if (terminalSize == null) { 207 return null; 208 } 209 updatePageSize(terminalSize); 210 return terminalSize; 211 } 212 213 private void updatePageSize(TerminalSize terminalSize) { 214 pageSize = terminalSize.getRows() - SUMMARY_ROW_NUM - 2; 215 if (pageSize < 0) { 216 pageSize = 0; 217 } 218 } 219 220 @Nullable 221 public Integer getPageSize() { 222 return pageSize; 223 } 224 225 public void showTopScreen(Summary summary, List<Header> headers, List<Record> records, 226 Record selectedRecord) { 227 showSummary(summary); 228 clearMessage(); 229 showHeaders(headers); 230 showRecords(headers, records, selectedRecord); 231 } 232 233 private void showSummary(Summary summary) { 234 TerminalPrinter printer = getTerminalPrinter(SUMMARY_START_ROW); 235 printer.print(String.format("HBase hbtop - %s", summary.getCurrentTime())).endOfLine(); 236 printer.print(String.format("Version: %s", summary.getVersion())).endOfLine(); 237 printer.print(String.format("Cluster ID: %s", summary.getClusterId())).endOfLine(); 238 printer.print("RegionServer(s): ").startBold().print(Integer.toString(summary.getServers())) 239 .stopBold().print(" total, ").startBold().print(Integer.toString(summary.getLiveServers())) 240 .stopBold().print(" live, ").startBold().print(Integer.toString(summary.getDeadServers())) 241 .stopBold().print(" dead").endOfLine(); 242 printer.print("RegionCount: ").startBold().print(Integer.toString(summary.getRegionCount())) 243 .stopBold().print(" total, ").startBold().print(Integer.toString(summary.getRitCount())) 244 .stopBold().print(" rit").endOfLine(); 245 printer.print("Average Cluster Load: ").startBold() 246 .print(String.format("%.2f", summary.getAverageLoad())).stopBold().endOfLine(); 247 printer.print("Aggregate Request/s: ").startBold() 248 .print(Long.toString(summary.getAggregateRequestPerSecond())).stopBold().endOfLine(); 249 } 250 251 private void showRecords(List<Header> headers, List<Record> records, Record selectedRecord) { 252 TerminalPrinter printer = getTerminalPrinter(RECORD_START_ROW); 253 int size; 254 if (pageSize != null) { 255 size = pageSize; 256 } else { 257 size = records.size(); 258 } 259 List<String> buf = new ArrayList<>(headers.size()); 260 for (int i = 0; i < size; i++) { 261 if (i < records.size()) { 262 Record record = records.get(i); 263 buf.clear(); 264 for (Header header : headers) { 265 String value = ""; 266 if (record.containsKey(header.getField())) { 267 value = record.get(header.getField()).asString(); 268 } 269 270 buf.add(limitLineLength(String.format(header.format(), value), header.getLength())); 271 } 272 273 String recordString = String.join(" ", buf); 274 if (!recordString.isEmpty()) { 275 recordString += " "; 276 } 277 278 if (record == selectedRecord) { 279 printer.startHighlight().print(recordString).stopHighlight().endOfLine(); 280 } else { 281 printer.print(recordString).endOfLine(); 282 } 283 } else { 284 printer.endOfLine(); 285 } 286 } 287 } 288 289 private void showHeaders(List<Header> headers) { 290 String header = headers.stream().map(h -> String.format(h.format(), h.getField().getHeader())) 291 .collect(Collectors.joining(" ")); 292 293 if (!header.isEmpty()) { 294 header += " "; 295 } 296 297 getTerminalPrinter(RECORD_HEADER_ROW).startHighlight().print(header).stopHighlight() 298 .endOfLine(); 299 } 300 301 private String limitLineLength(String line, int length) { 302 if (line.length() > length) { 303 return line.substring(0, length - 1) + "+"; 304 } 305 return line; 306 } 307 308 private void clearMessage() { 309 getTerminalPrinter(MESSAGE_ROW).print("").endOfLine(); 310 } 311 312 private ScreenView unknownCommandMessage() { 313 cancelTimer(); 314 return topScreenPresenter.goToMessageMode(getScreen(), getTerminal(), MESSAGE_ROW, 315 "Unknown command - try 'h' for help"); 316 } 317}