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; 019 020import java.util.ArrayList; 021import java.util.List; 022import java.util.Objects; 023import java.util.Optional; 024import java.util.stream.Collectors; 025import org.apache.hadoop.conf.Configuration; 026import org.apache.hadoop.conf.Configured; 027import org.apache.hadoop.hbase.HBaseConfiguration; 028import org.apache.hadoop.hbase.HBaseInterfaceAudience; 029import org.apache.hadoop.hbase.hbtop.field.Field; 030import org.apache.hadoop.hbase.hbtop.field.FieldInfo; 031import org.apache.hadoop.hbase.hbtop.mode.Mode; 032import org.apache.hadoop.hbase.hbtop.screen.Screen; 033import org.apache.hadoop.util.Tool; 034import org.apache.hadoop.util.ToolRunner; 035import org.apache.yetus.audience.InterfaceAudience; 036import org.slf4j.Logger; 037import org.slf4j.LoggerFactory; 038 039import org.apache.hbase.thirdparty.com.google.common.base.Splitter; 040import org.apache.hbase.thirdparty.org.apache.commons.cli.CommandLine; 041import org.apache.hbase.thirdparty.org.apache.commons.cli.DefaultParser; 042import org.apache.hbase.thirdparty.org.apache.commons.cli.HelpFormatter; 043import org.apache.hbase.thirdparty.org.apache.commons.cli.Options; 044 045/** 046 * A real-time monitoring tool for HBase like Unix top command. 047 */ 048@InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.TOOLS) 049public class HBTop extends Configured implements Tool { 050 051 private static final Logger LOGGER = LoggerFactory.getLogger(HBTop.class); 052 053 public HBTop() { 054 this(HBaseConfiguration.create()); 055 } 056 057 public HBTop(Configuration conf) { 058 super(Objects.requireNonNull(conf)); 059 } 060 061 @Override 062 public int run(String[] args) throws Exception { 063 long initialRefreshDelay = 3 * 1000; 064 Mode initialMode = Mode.REGION; 065 List<Field> initialFields = null; 066 Field initialSortField = null; 067 Boolean initialAscendingSort = null; 068 List<RecordFilter> initialFilters = null; 069 long numberOfIterations = Long.MAX_VALUE; 070 boolean batchMode = false; 071 try { 072 Options opts = getOptions(); 073 CommandLine commandLine = new DefaultParser().parse(opts, args); 074 075 if (commandLine.hasOption("help")) { 076 printUsage(opts); 077 return 0; 078 } 079 080 if (commandLine.hasOption("mode")) { 081 String mode = commandLine.getOptionValue("mode"); 082 switch (mode) { 083 case "n": 084 initialMode = Mode.NAMESPACE; 085 break; 086 087 case "t": 088 initialMode = Mode.TABLE; 089 break; 090 091 case "r": 092 initialMode = Mode.REGION; 093 break; 094 095 case "s": 096 initialMode = Mode.REGION_SERVER; 097 break; 098 099 case "u": 100 initialMode = Mode.USER; 101 break; 102 103 case "c": 104 initialMode = Mode.CLIENT; 105 break; 106 107 default: 108 LOGGER.warn("Mode set invalid, using default"); 109 break; 110 } 111 } 112 113 if (commandLine.hasOption("outputFieldNames")) { 114 initialMode.getFieldInfos().forEach(f -> System.out.println(f.getField().getHeader())); 115 return 0; 116 } 117 118 if (commandLine.hasOption("delay")) { 119 int delay = 0; 120 try { 121 delay = Integer.parseInt(commandLine.getOptionValue("delay")); 122 } catch (NumberFormatException ignored) { 123 // Deliberately ignored, we handle the issue below. 124 } 125 126 if (delay < 1) { 127 LOGGER.warn("Delay set too low or invalid, using default"); 128 } else { 129 initialRefreshDelay = delay * 1000L; 130 } 131 } 132 133 if (commandLine.hasOption("numberOfIterations")) { 134 try { 135 numberOfIterations = Long.parseLong(commandLine.getOptionValue("numberOfIterations")); 136 } catch (NumberFormatException ignored) { 137 LOGGER.warn("The number of iterations set invalid, ignoring"); 138 } 139 } 140 141 if (commandLine.hasOption("sortField")) { 142 String sortField = commandLine.getOptionValue("sortField"); 143 144 String field; 145 boolean ascendingSort; 146 if (sortField.startsWith("+")) { 147 field = sortField.substring(1); 148 ascendingSort = false; 149 } else if (sortField.startsWith("-")) { 150 field = sortField.substring(1); 151 ascendingSort = true; 152 } else { 153 field = sortField; 154 ascendingSort = false; 155 } 156 157 Optional<FieldInfo> fieldInfo = initialMode.getFieldInfos().stream() 158 .filter(f -> f.getField().getHeader().equals(field)).findFirst(); 159 if (fieldInfo.isPresent()) { 160 initialSortField = fieldInfo.get().getField(); 161 initialAscendingSort = ascendingSort; 162 } else { 163 LOGGER.warn("The specified sort field " + field + " is not found, using default"); 164 } 165 } 166 167 if (commandLine.hasOption("fields")) { 168 Iterable<String> fields = Splitter.on(',').split(commandLine.getOptionValue("fields")); 169 initialFields = new ArrayList<>(); 170 for (String field : fields) { 171 Optional<FieldInfo> fieldInfo = initialMode.getFieldInfos().stream() 172 .filter(f -> f.getField().getHeader().equals(field)).findFirst(); 173 if (fieldInfo.isPresent()) { 174 initialFields.add(fieldInfo.get().getField()); 175 } else { 176 LOGGER.warn("The specified field " + field + " is not found, ignoring"); 177 } 178 } 179 } 180 181 if (commandLine.hasOption("filters")) { 182 Iterable<String> filters = Splitter.on(',').split(commandLine.getOptionValue("filters")); 183 List<Field> fields = initialMode.getFieldInfos().stream().map(FieldInfo::getField) 184 .collect(Collectors.toList()); 185 for (String filter : filters) { 186 RecordFilter f = RecordFilter.parse(filter, fields, false); 187 if (f != null) { 188 if (initialFilters == null) { 189 initialFilters = new ArrayList<>(); 190 } 191 initialFilters.add(f); 192 } else { 193 LOGGER.warn("The specified filter " + filter + " is invalid, ignoring"); 194 } 195 } 196 } 197 198 if (commandLine.hasOption("batchMode")) { 199 batchMode = true; 200 } 201 } catch (Exception e) { 202 LOGGER.error("Unable to parse options", e); 203 return 1; 204 } 205 206 try (Screen screen = new Screen(getConf(), initialRefreshDelay, initialMode, initialFields, 207 initialSortField, initialAscendingSort, initialFilters, numberOfIterations, batchMode)) { 208 screen.run(); 209 } 210 211 return 0; 212 } 213 214 private Options getOptions() { 215 Options opts = new Options(); 216 opts.addOption("h", "help", false, "Print usage; for help while the tool is running press 'h'"); 217 opts.addOption("d", "delay", true, "The refresh delay (in seconds); default is 3 seconds"); 218 opts.addOption("m", "mode", true, 219 "The mode; n (Namespace)|t (Table)|r (Region)|s (RegionServer)|u (User)" 220 + "|c (Client), default is r"); 221 opts.addOption("n", "numberOfIterations", true, "The number of iterations"); 222 opts.addOption("s", "sortField", true, 223 "The initial sort field. You can prepend a `+' or `-' to the field name to also override" 224 + " the sort direction. A leading `+' will force sorting high to low, whereas a `-' will" 225 + " ensure a low to high ordering"); 226 opts.addOption("O", "outputFieldNames", false, 227 "Print each of the available field names on a separate line, then quit"); 228 opts.addOption("f", "fields", true, 229 "Show only the given fields. Specify comma separated fields to show multiple fields"); 230 opts.addOption("i", "filters", true, 231 "The initial filters. Specify comma separated filters to set multiple filters"); 232 opts.addOption("b", "batchMode", false, 233 "Starts hbtop in Batch mode, which could be useful for sending output from hbtop to other" 234 + " programs or to a file. In this mode, hbtop will not accept input and runs until the" 235 + " iterations limit you've set with the `-n' command-line option or until killed"); 236 return opts; 237 } 238 239 private void printUsage(Options opts) { 240 new HelpFormatter().printHelp("hbase hbtop [opts] [-D<property=value>]*", opts); 241 System.out.println(""); 242 System.out.println(" Note: -D properties will be applied to the conf used."); 243 System.out.println(" For example:"); 244 System.out.println(" -Dhbase.client.zookeeper.quorum=<zookeeper quorum>"); 245 System.out.println(" -Dzookeeper.znode.parent=<znode parent>"); 246 System.out.println(""); 247 } 248 249 public static void main(String[] args) throws Exception { 250 int res = ToolRunner.run(new HBTop(), args); 251 System.exit(res); 252 } 253}