View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.hbase.hbtop.screen.top;
19  
20  import edu.umd.cs.findbugs.annotations.Nullable;
21  import java.util.ArrayList;
22  import java.util.List;
23  
24  import org.apache.commons.lang3.StringUtils;
25  import org.apache.hadoop.hbase.classification.InterfaceAudience;
26  import org.apache.hadoop.hbase.client.Admin;
27  import org.apache.hadoop.hbase.hbtop.Record;
28  import org.apache.hadoop.hbase.hbtop.RecordFilter;
29  import org.apache.hadoop.hbase.hbtop.field.Field;
30  import org.apache.hadoop.hbase.hbtop.mode.Mode;
31  import org.apache.hadoop.hbase.hbtop.screen.AbstractScreenView;
32  import org.apache.hadoop.hbase.hbtop.screen.Screen;
33  import org.apache.hadoop.hbase.hbtop.screen.ScreenView;
34  import org.apache.hadoop.hbase.hbtop.terminal.KeyPress;
35  import org.apache.hadoop.hbase.hbtop.terminal.Terminal;
36  import org.apache.hadoop.hbase.hbtop.terminal.TerminalPrinter;
37  import org.apache.hadoop.hbase.hbtop.terminal.TerminalSize;
38  
39  /**
40   * The screen that provides a dynamic real-time view for the HBase metrics.
41   *
42   * This shows the metric {@link Summary} and the metric {@link Record}s. The summary and the
43   * metrics are updated periodically (3 seconds by default).
44   */
45  @InterfaceAudience.Private
46  public class TopScreenView extends AbstractScreenView {
47  
48    private static final int SUMMARY_START_ROW = 0;
49    private static final int SUMMARY_ROW_NUM = 7;
50    private static final int MESSAGE_ROW = 7;
51    private static final int RECORD_HEADER_ROW = 8;
52    private static final int RECORD_START_ROW = 9;
53  
54    private final TopScreenPresenter topScreenPresenter;
55    private Integer pageSize;
56  
57    public TopScreenView(Screen screen, Terminal terminal, long initialRefreshDelay, Admin admin,
58      Mode initialMode, @Nullable List<Field> initialFields, @Nullable Field initialSortField,
59      @Nullable Boolean initialAscendingSort, @Nullable List<RecordFilter> initialFilters,
60      long numberOfIterations) {
61      super(screen, terminal);
62      this.topScreenPresenter = new TopScreenPresenter(this, initialRefreshDelay,
63        new TopScreenModel(admin, initialMode, initialFields, initialSortField,
64          initialAscendingSort, initialFilters), initialFields, numberOfIterations);
65    }
66  
67    @Override
68    public void init() {
69      topScreenPresenter.init();
70      long delay = topScreenPresenter.refresh(true);
71      setTimer(delay);
72    }
73  
74    @Nullable
75    @Override
76    public ScreenView handleTimer() {
77      long delay = topScreenPresenter.refresh(false);
78      setTimer(delay);
79      return topScreenPresenter.isIterationFinished() ? null : this;
80    }
81  
82    @Nullable
83    @Override
84    public ScreenView handleKeyPress(KeyPress keyPress) {
85      switch (keyPress.getType()) {
86        case Enter:
87          topScreenPresenter.refresh(true);
88          return topScreenPresenter.isIterationFinished() ? null : this;
89  
90        case ArrowUp:
91          topScreenPresenter.arrowUp();
92          return topScreenPresenter.isIterationFinished() ? null : this;
93  
94        case ArrowDown:
95          topScreenPresenter.arrowDown();
96          return topScreenPresenter.isIterationFinished() ? null : this;
97  
98        case ArrowLeft:
99          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 = StringUtils.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     List<String> headerStrings = new ArrayList<>();
297     for (Header header : headers) {
298       headerStrings.add(String.format(header.format(), header.getField().getHeader()));
299     }
300     String header = StringUtils.join(headerStrings, " ");
301 
302     if (!header.isEmpty()) {
303       header += " ";
304     }
305 
306     getTerminalPrinter(RECORD_HEADER_ROW).startHighlight().print(header).stopHighlight()
307       .endOfLine();
308   }
309 
310   private String limitLineLength(String line, int length) {
311     if (line.length() > length) {
312       return line.substring(0, length - 1) + "+";
313     }
314     return line;
315   }
316 
317   private void clearMessage() {
318     getTerminalPrinter(MESSAGE_ROW).print("").endOfLine();
319   }
320 
321   private ScreenView unknownCommandMessage() {
322     cancelTimer();
323     return topScreenPresenter.goToMessageMode(getScreen(), getTerminal(), MESSAGE_ROW,
324       "Unknown command - try 'h' for help");
325   }
326 }