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/** 040 * The screen that provides a dynamic real-time view for the HBase metrics. 041 * 042 * This shows the metric {@link Summary} and the metric {@link Record}s. The summary and the 043 * metrics are updated periodically (3 seconds by default). 044 */ 045@InterfaceAudience.Private 046public class TopScreenView extends AbstractScreenView { 047 048 private static final int SUMMARY_START_ROW = 0; 049 private static final int SUMMARY_ROW_NUM = 7; 050 private static final int MESSAGE_ROW = 7; 051 private static final int RECORD_HEADER_ROW = 8; 052 private static final int RECORD_START_ROW = 9; 053 054 private final TopScreenPresenter topScreenPresenter; 055 private Integer pageSize; 056 057 public TopScreenView(Screen screen, Terminal terminal, long initialRefreshDelay, Admin admin, 058 Mode initialMode, @Nullable List<Field> initialFields, @Nullable Field initialSortField, 059 @Nullable Boolean initialAscendingSort, @Nullable List<RecordFilter> initialFilters, 060 long numberOfIterations) { 061 super(screen, terminal); 062 this.topScreenPresenter = new TopScreenPresenter(this, initialRefreshDelay, 063 new TopScreenModel(admin, initialMode, initialFields, initialSortField, 064 initialAscendingSort, initialFilters), 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): ") 239 .startBold().print(Integer.toString(summary.getServers())).stopBold() 240 .print(" total, ") 241 .startBold().print(Integer.toString(summary.getLiveServers())).stopBold() 242 .print(" live, ") 243 .startBold().print(Integer.toString(summary.getDeadServers())).stopBold() 244 .print(" dead").endOfLine(); 245 printer.print("RegionCount: ") 246 .startBold().print(Integer.toString(summary.getRegionCount())).stopBold() 247 .print(" total, ") 248 .startBold().print(Integer.toString(summary.getRitCount())).stopBold() 249 .print(" rit").endOfLine(); 250 printer.print("Average Cluster Load: ") 251 .startBold().print(String.format("%.2f", summary.getAverageLoad())).stopBold().endOfLine(); 252 printer.print("Aggregate Request/s: ") 253 .startBold().print(Long.toString(summary.getAggregateRequestPerSecond())).stopBold() 254 .endOfLine(); 255 } 256 257 private void showRecords(List<Header> headers, List<Record> records, Record selectedRecord) { 258 TerminalPrinter printer = getTerminalPrinter(RECORD_START_ROW); 259 int size; 260 if (pageSize != null) { 261 size = pageSize; 262 } else { 263 size = records.size(); 264 } 265 List<String> buf = new ArrayList<>(headers.size()); 266 for (int i = 0; i < size; i++) { 267 if(i < records.size()) { 268 Record record = records.get(i); 269 buf.clear(); 270 for (Header header : headers) { 271 String value = ""; 272 if (record.containsKey(header.getField())) { 273 value = record.get(header.getField()).asString(); 274 } 275 276 buf.add(limitLineLength(String.format(header.format(), value), header.getLength())); 277 } 278 279 String recordString = String.join(" ", buf); 280 if (!recordString.isEmpty()) { 281 recordString += " "; 282 } 283 284 if (record == selectedRecord) { 285 printer.startHighlight().print(recordString).stopHighlight().endOfLine(); 286 } else { 287 printer.print(recordString).endOfLine(); 288 } 289 } else { 290 printer.endOfLine(); 291 } 292 } 293 } 294 295 private void showHeaders(List<Header> headers) { 296 String header = headers.stream() 297 .map(h -> String.format(h.format(), h.getField().getHeader())) 298 .collect(Collectors.joining(" ")); 299 300 if (!header.isEmpty()) { 301 header += " "; 302 } 303 304 getTerminalPrinter(RECORD_HEADER_ROW).startHighlight().print(header).stopHighlight() 305 .endOfLine(); 306 } 307 308 private String limitLineLength(String line, int length) { 309 if (line.length() > length) { 310 return line.substring(0, length - 1) + "+"; 311 } 312 return line; 313 } 314 315 private void clearMessage() { 316 getTerminalPrinter(MESSAGE_ROW).print("").endOfLine(); 317 } 318 319 private ScreenView unknownCommandMessage() { 320 cancelTimer(); 321 return topScreenPresenter.goToMessageMode(getScreen(), getTerminal(), MESSAGE_ROW, 322 "Unknown command - try 'h' for help"); 323 } 324}