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}