You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@accumulo.apache.org by ct...@apache.org on 2013/11/01 01:56:11 UTC
[32/54] [partial] ACCUMULO-658,
ACCUMULO-656 Split server into separate modules
http://git-wip-us.apache.org/repos/asf/accumulo/blob/598821cd/server/monitor/src/main/java/org/apache/accumulo/monitor/servlets/TServersServlet.java
----------------------------------------------------------------------
diff --git a/server/monitor/src/main/java/org/apache/accumulo/monitor/servlets/TServersServlet.java b/server/monitor/src/main/java/org/apache/accumulo/monitor/servlets/TServersServlet.java
new file mode 100644
index 0000000..681e696
--- /dev/null
+++ b/server/monitor/src/main/java/org/apache/accumulo/monitor/servlets/TServersServlet.java
@@ -0,0 +1,369 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.accumulo.monitor.servlets;
+
+import java.lang.management.ManagementFactory;
+import java.security.MessageDigest;
+import java.text.DateFormat;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map.Entry;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.accumulo.core.data.KeyExtent;
+import org.apache.accumulo.core.master.thrift.DeadServer;
+import org.apache.accumulo.core.master.thrift.MasterMonitorInfo;
+import org.apache.accumulo.core.master.thrift.TableInfo;
+import org.apache.accumulo.core.master.thrift.TabletServerStatus;
+import org.apache.accumulo.core.tabletserver.thrift.ActionStats;
+import org.apache.accumulo.core.tabletserver.thrift.TabletClientService;
+import org.apache.accumulo.core.tabletserver.thrift.TabletStats;
+import org.apache.accumulo.core.util.Duration;
+import org.apache.accumulo.core.util.ThriftUtil;
+import org.apache.accumulo.monitor.Monitor;
+import org.apache.accumulo.monitor.util.Table;
+import org.apache.accumulo.monitor.util.TableRow;
+import org.apache.accumulo.monitor.util.celltypes.CompactionsType;
+import org.apache.accumulo.monitor.util.celltypes.DateTimeType;
+import org.apache.accumulo.monitor.util.celltypes.DurationType;
+import org.apache.accumulo.monitor.util.celltypes.NumberType;
+import org.apache.accumulo.monitor.util.celltypes.PercentageType;
+import org.apache.accumulo.monitor.util.celltypes.ProgressChartType;
+import org.apache.accumulo.monitor.util.celltypes.TServerLinkType;
+import org.apache.accumulo.monitor.util.celltypes.TableLinkType;
+import org.apache.accumulo.server.master.state.TabletServerState;
+import org.apache.accumulo.server.security.SystemCredentials;
+import org.apache.accumulo.server.util.ActionStatsUpdator;
+import org.apache.accumulo.server.util.TableInfoUtil;
+import org.apache.accumulo.trace.instrument.Tracer;
+import org.apache.commons.codec.binary.Base64;
+
+import com.google.common.net.HostAndPort;
+
+public class TServersServlet extends BasicServlet {
+
+ private static final long serialVersionUID = 1L;
+ private static final TabletServerStatus NO_STATUS = new TabletServerStatus();
+
+ static class SecondType extends NumberType<Double> {
+
+ @Override
+ public String format(Object obj) {
+ if (obj == null)
+ return "—";
+ return Duration.format((long) (1000.0 * (Double) obj));
+ }
+
+ }
+
+ @Override
+ protected String getTitle(HttpServletRequest req) {
+ return "Tablet Server Status";
+ }
+
+ @Override
+ protected void pageBody(HttpServletRequest req, HttpServletResponse response, StringBuilder sb) throws Exception {
+ String tserverAddress = req.getParameter("s");
+
+ // Check to make sure tserver is a known address
+ boolean tserverExists = false;
+ if (tserverAddress != null && tserverAddress.isEmpty() == false) {
+ for (TabletServerStatus ts : Monitor.getMmi().getTServerInfo()) {
+ if (tserverAddress.equals(ts.getName())) {
+ tserverExists = true;
+ break;
+ }
+ }
+ }
+
+ if (tserverAddress == null || tserverAddress.isEmpty() || tserverExists == false) {
+ doBadTserverList(req, sb);
+
+ doDeadTserverList(req, sb);
+
+ ArrayList<TabletServerStatus> tservers = new ArrayList<TabletServerStatus>();
+ if (Monitor.getMmi() != null)
+ tservers.addAll(Monitor.getMmi().tServerInfo);
+
+ Table tServerList = new Table("tservers", "Tablet Servers");
+ tServerList.setSubCaption("Click on the <span style='color: #0000ff;'>server address</span> to view detailed performance statistics for that server.");
+
+ doTserverList(req, sb, tservers, null, tServerList);
+ return;
+ }
+
+ double totalElapsedForAll = 0;
+ double splitStdDev = 0;
+ double minorStdDev = 0;
+ double minorQueueStdDev = 0;
+ double majorStdDev = 0;
+ double majorQueueStdDev = 0;
+ double currentMinorAvg = 0;
+ double currentMajorAvg = 0;
+ double currentMinorStdDev = 0;
+ double currentMajorStdDev = 0;
+ TabletStats total = new TabletStats(null, new ActionStats(), new ActionStats(), new ActionStats(), 0, 0, 0, 0);
+
+ HostAndPort address = HostAndPort.fromString(tserverAddress);
+ TabletStats historical = new TabletStats(null, new ActionStats(), new ActionStats(), new ActionStats(), 0, 0, 0, 0);
+ List<TabletStats> tsStats = new ArrayList<TabletStats>();
+ try {
+ TabletClientService.Client client = ThriftUtil.getClient(new TabletClientService.Client.Factory(), address, Monitor.getSystemConfiguration());
+ try {
+ for (String tableId : Monitor.getMmi().tableMap.keySet()) {
+ tsStats.addAll(client.getTabletStats(Tracer.traceInfo(), SystemCredentials.get().toThrift(Monitor.getInstance()), tableId));
+ }
+ historical = client.getHistoricalStats(Tracer.traceInfo(), SystemCredentials.get().toThrift(Monitor.getInstance()));
+ } finally {
+ ThriftUtil.returnClient(client);
+ }
+ } catch (Exception e) {
+ banner(sb, "error", "No Such Tablet ServerAvailable");
+ log.error(e, e);
+ return;
+ }
+
+ Table perTabletResults = new Table("perTabletResults", "Detailed Current Operations");
+ perTabletResults.setSubCaption("Per-tablet Details");
+ perTabletResults.addSortableColumn("Table", new TableLinkType(), null);
+ perTabletResults.addSortableColumn("Tablet");
+ perTabletResults.addSortableColumn("Entries", new NumberType<Long>(), null);
+ perTabletResults.addSortableColumn("Ingest", new NumberType<Long>(), null);
+ perTabletResults.addSortableColumn("Query", new NumberType<Long>(), null);
+ perTabletResults.addSortableColumn("Minor Avg", new SecondType(), null);
+ perTabletResults.addSortableColumn("Minor Std Dev", new SecondType(), null);
+ perTabletResults.addSortableColumn("Minor Avg e/s", new NumberType<Double>(), null);
+ perTabletResults.addSortableColumn("Major Avg", new SecondType(), null);
+ perTabletResults.addSortableColumn("Major Std Dev", new SecondType(), null);
+ perTabletResults.addSortableColumn("Major Avg e/s", new NumberType<Double>(), null);
+
+ for (TabletStats info : tsStats) {
+ if (info.extent == null) {
+ historical = info;
+ continue;
+ }
+ total.numEntries += info.numEntries;
+ ActionStatsUpdator.update(total.minors, info.minors);
+ ActionStatsUpdator.update(total.majors, info.majors);
+
+ KeyExtent extent = new KeyExtent(info.extent);
+ String tableId = extent.getTableId().toString();
+ MessageDigest digester = MessageDigest.getInstance("MD5");
+ if (extent.getEndRow() != null && extent.getEndRow().getLength() > 0) {
+ digester.update(extent.getEndRow().getBytes(), 0, extent.getEndRow().getLength());
+ }
+ String obscuredExtent = new String(Base64.encodeBase64(digester.digest()));
+ String displayExtent = String.format("<code>[%s]</code>", obscuredExtent);
+
+ TableRow row = perTabletResults.prepareRow();
+ row.add(tableId);
+ row.add(displayExtent);
+ row.add(info.numEntries);
+ row.add(info.ingestRate);
+ row.add(info.queryRate);
+ row.add(info.minors.num != 0 ? info.minors.elapsed / info.minors.num : null);
+ row.add(stddev(info.minors.elapsed, info.minors.num, info.minors.sumDev));
+ row.add(info.minors.elapsed != 0 ? info.minors.count / info.minors.elapsed : null);
+ row.add(info.majors.num != 0 ? info.majors.elapsed / info.majors.num : null);
+ row.add(stddev(info.majors.elapsed, info.majors.num, info.majors.sumDev));
+ row.add(info.majors.elapsed != 0 ? info.majors.count / info.majors.elapsed : null);
+ perTabletResults.addRow(row);
+ }
+
+ // Calculate current averages oldServer adding in historical data
+ if (total.minors.num != 0)
+ currentMinorAvg = (long) (total.minors.elapsed / total.minors.num);
+ if (total.minors.elapsed != 0 && total.minors.num != 0)
+ currentMinorStdDev = stddev(total.minors.elapsed, total.minors.num, total.minors.sumDev);
+ if (total.majors.num != 0)
+ currentMajorAvg = total.majors.elapsed / total.majors.num;
+ if (total.majors.elapsed != 0 && total.majors.num != 0 && total.majors.elapsed > total.majors.num)
+ currentMajorStdDev = stddev(total.majors.elapsed, total.majors.num, total.majors.sumDev);
+
+ // After these += operations, these variables are now total for current
+ // tablets and historical tablets
+ ActionStatsUpdator.update(total.minors, historical.minors);
+ ActionStatsUpdator.update(total.majors, historical.majors);
+ totalElapsedForAll += total.majors.elapsed + historical.splits.elapsed + total.minors.elapsed;
+
+ minorStdDev = stddev(total.minors.elapsed, total.minors.num, total.minors.sumDev);
+ minorQueueStdDev = stddev(total.minors.queueTime, total.minors.num, total.minors.queueSumDev);
+ majorStdDev = stddev(total.majors.elapsed, total.majors.num, total.majors.sumDev);
+ majorQueueStdDev = stddev(total.majors.queueTime, total.majors.num, total.majors.queueSumDev);
+ splitStdDev = stddev(historical.splits.num, historical.splits.elapsed, historical.splits.sumDev);
+
+ doDetailTable(req, sb, address, tsStats.size(), total, historical);
+ doAllTimeTable(req, sb, total, historical, majorQueueStdDev, minorQueueStdDev, totalElapsedForAll, splitStdDev, majorStdDev, minorStdDev);
+ doCurrentTabletOps(req, sb, currentMinorAvg, currentMinorStdDev, currentMajorAvg, currentMajorStdDev);
+ perTabletResults.generate(req, sb);
+ }
+
+ private void doCurrentTabletOps(HttpServletRequest req, StringBuilder sb, double currentMinorAvg, double currentMinorStdDev, double currentMajorAvg,
+ double currentMajorStdDev) {
+ Table currentTabletOps = new Table("currentTabletOps", "Current Tablet Operation Results");
+ currentTabletOps.addSortableColumn("Minor Average", new SecondType(), null);
+ currentTabletOps.addSortableColumn("Minor Std Dev", new SecondType(), null);
+ currentTabletOps.addSortableColumn("Major Avg", new SecondType(), null);
+ currentTabletOps.addSortableColumn("Major Std Dev", new SecondType(), null);
+ currentTabletOps.addRow(currentMinorAvg, currentMinorStdDev, currentMajorAvg, currentMajorStdDev);
+ currentTabletOps.generate(req, sb);
+ }
+
+ private void doAllTimeTable(HttpServletRequest req, StringBuilder sb, TabletStats total, TabletStats historical, double majorQueueStdDev,
+ double minorQueueStdDev, double totalElapsedForAll, double splitStdDev, double majorStdDev, double minorStdDev) {
+
+ Table opHistoryDetails = new Table("opHistoryDetails", "All-Time Tablet Operation Results");
+ opHistoryDetails.addSortableColumn("Operation");
+ opHistoryDetails.addSortableColumn("Success", new NumberType<Integer>(), null);
+ opHistoryDetails.addSortableColumn("Failure", new NumberType<Integer>(), null);
+ opHistoryDetails.addSortableColumn("Average<br />Queue Time", new SecondType(), null);
+ opHistoryDetails.addSortableColumn("Std. Dev.<br />Queue Time", new SecondType(), null);
+ opHistoryDetails.addSortableColumn("Average<br />Time", new SecondType(), null);
+ opHistoryDetails.addSortableColumn("Std. Dev.<br />Time", new SecondType(), null);
+ opHistoryDetails.addSortableColumn("Percentage Time Spent", new ProgressChartType(totalElapsedForAll), null);
+
+ opHistoryDetails.addRow("Split", historical.splits.num, historical.splits.fail, null, null,
+ historical.splits.num != 0 ? (historical.splits.elapsed / historical.splits.num) : null, splitStdDev, historical.splits.elapsed);
+ opHistoryDetails.addRow("Major Compaction", total.majors.num, total.majors.fail, total.majors.num != 0 ? (total.majors.queueTime / total.majors.num)
+ : null, majorQueueStdDev, total.majors.num != 0 ? (total.majors.elapsed / total.majors.num) : null, majorStdDev, total.majors.elapsed);
+ opHistoryDetails.addRow("Minor Compaction", total.minors.num, total.minors.fail, total.minors.num != 0 ? (total.minors.queueTime / total.minors.num)
+ : null, minorQueueStdDev, total.minors.num != 0 ? (total.minors.elapsed / total.minors.num) : null, minorStdDev, total.minors.elapsed);
+ opHistoryDetails.generate(req, sb);
+ }
+
+ private void doDetailTable(HttpServletRequest req, StringBuilder sb, HostAndPort address, int numTablets, TabletStats total, TabletStats historical) {
+ Table detailTable = new Table("tServerDetail", "Details");
+ detailTable.setSubCaption(address.getHostText() + ":" + address.getPort());
+ detailTable.addSortableColumn("Hosted Tablets", new NumberType<Integer>(), null);
+ detailTable.addSortableColumn("Entries", new NumberType<Long>(), null);
+ detailTable.addSortableColumn("Minor Compacting", new NumberType<Integer>(), null);
+ detailTable.addSortableColumn("Major Compacting", new NumberType<Integer>(), null);
+ detailTable.addSortableColumn("Splitting", new NumberType<Integer>(), null);
+ detailTable.addRow(numTablets, total.numEntries, total.minors.status, total.majors.status, historical.splits.status);
+ detailTable.generate(req, sb);
+ }
+
+ /*
+ * omg there's so much undocumented stuff going on here. First, sumDev is a partial standard deviation computation. It is the (clue 1) sum of the squares of
+ * (clue 2) seconds of elapsed time.
+ */
+ private static double stddev(double elapsed, double num, double sumDev) {
+ if (num != 0) {
+ double average = elapsed / num;
+ return Math.sqrt((sumDev / num) - (average * average));
+ }
+ return 0;
+ }
+
+ private void doBadTserverList(HttpServletRequest req, StringBuilder sb) {
+ if (Monitor.getMmi() != null && !Monitor.getMmi().badTServers.isEmpty()) {
+ Table badTServerList = new Table("badtservers", "Non-Functioning Tablet Servers", "error");
+ badTServerList.setSubCaption("The following tablet servers reported a status other than Online.");
+ badTServerList.addSortableColumn("Tablet Server");
+ badTServerList.addSortableColumn("Tablet Server Status");
+ for (Entry<String,Byte> badserver : Monitor.getMmi().badTServers.entrySet())
+ badTServerList.addRow(badserver.getKey(), TabletServerState.getStateById(badserver.getValue()).name());
+ badTServerList.generate(req, sb);
+ }
+ }
+
+ private void doDeadTserverList(HttpServletRequest req, StringBuilder sb) {
+ MasterMonitorInfo mmi = Monitor.getMmi();
+ if (mmi != null) {
+ List<DeadServer> obit = mmi.deadTabletServers;
+ Table deadTServerList = new Table("deaddtservers", "Dead Tablet Servers", "error");
+ deadTServerList.setSubCaption("The following tablet servers are no longer reachable.");
+ doDeadServerTable(req, sb, deadTServerList, obit);
+ }
+ }
+
+ public static void doDeadServerTable(HttpServletRequest req, StringBuilder sb, Table deadTServerList, List<DeadServer> obit) {
+ if (obit != null && !obit.isEmpty()) {
+ deadTServerList.addSortableColumn("Server");
+ deadTServerList.addSortableColumn("Last Updated", new DateTimeType(DateFormat.MEDIUM, DateFormat.SHORT), null);
+ deadTServerList.addSortableColumn("Event");
+ deadTServerList.addUnsortableColumn("Clear");
+ for (DeadServer dead : obit)
+ deadTServerList.addRow(TServerLinkType.displayName(dead.server), dead.lastStatus, dead.status, "<a href='/op?action=clearDeadServer&redir="
+ + currentPage(req) + "&server=" + encode(dead.server) + "'>clear</a>");
+ deadTServerList.generate(req, sb);
+ }
+ }
+
+ static void doTserverList(HttpServletRequest req, StringBuilder sb, List<TabletServerStatus> tservers, String tableId, Table tServerList) {
+ int guessHighLoad = ManagementFactory.getOperatingSystemMXBean().getAvailableProcessors();
+ long now = System.currentTimeMillis();
+
+ double avgLastContact = 0.;
+ for (TabletServerStatus status : tservers) {
+ avgLastContact += (now - status.lastContact);
+ }
+ final long MINUTES = 3 * 60 * 1000;
+ tServerList.addSortableColumn("Server", new TServerLinkType(), null);
+ tServerList.addSortableColumn("Hosted Tablets", new NumberType<Integer>(0, Integer.MAX_VALUE), null);
+ tServerList.addSortableColumn("Last Contact", new DurationType(0l, (long) Math.min(avgLastContact * 4, MINUTES)), null);
+ tServerList.addSortableColumn("Entries", new NumberType<Long>(), "The number of key/value pairs.");
+ tServerList.addSortableColumn("Ingest", new NumberType<Long>(), "The number of key/value pairs inserted. (Note that deletes are also 'inserted')");
+ tServerList.addSortableColumn("Query", new NumberType<Long>(), "The number of key/value pairs returned to clients. (Not the number of scans)");
+ tServerList.addSortableColumn("Hold Time", new DurationType(), "The amount of time ingest is suspended waiting for data to be written to disk.");
+ tServerList.addSortableColumn("Running<br />Scans", new CompactionsType("scans"), "The number of scans running and queued on this tablet server.");
+ tServerList
+ .addSortableColumn(
+ "Minor<br />Compactions",
+ new CompactionsType("minor"),
+ "The number of minor compactions running and (queued waiting for resources). Minor compactions are the operations where entries are flushed from memory to disk.");
+ tServerList.addSortableColumn("Major<br />Compactions", new CompactionsType("major"),
+ "The number of major compactions running and (queued waiting for resources). "
+ + "Major compactions are the operations where many smaller files are grouped into a larger file, eliminating duplicates and cleaning up deletes.");
+ tServerList.addSortableColumn("Index Cache<br />Hit Rate", new PercentageType(), "The recent index cache hit rate.");
+ tServerList.addSortableColumn("Data Cache<br />Hit Rate", new PercentageType(), "The recent data cache hit rate.");
+ tServerList.addSortableColumn("OS Load", new NumberType<Double>(0., guessHighLoad * 1., 0., guessHighLoad * 3.),
+ "The Unix one minute load average. The average number of processes in the run queue over a one minute interval.");
+
+ log.debug("tableId: " + tableId);
+ for (TabletServerStatus status : tservers) {
+ if (status == null)
+ status = NO_STATUS;
+ TableInfo summary = TableInfoUtil.summarizeTableStats(status);
+ if (tableId != null)
+ summary = status.tableMap.get(tableId);
+ if (summary == null)
+ continue;
+ TableRow row = tServerList.prepareRow();
+ row.add(status); // add for server name
+ row.add(summary.tablets);
+ row.add(now - status.lastContact);
+ row.add(summary.recs);
+ row.add(summary.ingestRate);
+ row.add(summary.queryRate);
+ row.add(status.holdTime);
+ row.add(summary); // add for scans
+ row.add(summary); // add for minor compactions
+ row.add(summary); // add for major compactions
+ double indexCacheHitRate = status.indexCacheHits / (double) Math.max(status.indexCacheRequest, 1);
+ row.add(indexCacheHitRate);
+ double dataCacheHitRate = status.dataCacheHits / (double) Math.max(status.dataCacheRequest, 1);
+ row.add(dataCacheHitRate);
+ row.add(status.osLoad);
+ tServerList.addRow(row);
+ }
+ tServerList.generate(req, sb);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/accumulo/blob/598821cd/server/monitor/src/main/java/org/apache/accumulo/monitor/servlets/TablesServlet.java
----------------------------------------------------------------------
diff --git a/server/monitor/src/main/java/org/apache/accumulo/monitor/servlets/TablesServlet.java b/server/monitor/src/main/java/org/apache/accumulo/monitor/servlets/TablesServlet.java
new file mode 100644
index 0000000..428880e
--- /dev/null
+++ b/server/monitor/src/main/java/org/apache/accumulo/monitor/servlets/TablesServlet.java
@@ -0,0 +1,190 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.accumulo.monitor.servlets;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.accumulo.core.client.Instance;
+import org.apache.accumulo.core.client.impl.Tables;
+import org.apache.accumulo.core.data.KeyExtent;
+import org.apache.accumulo.core.data.Range;
+import org.apache.accumulo.core.master.thrift.TableInfo;
+import org.apache.accumulo.core.master.thrift.TabletServerStatus;
+import org.apache.accumulo.core.metadata.MetadataTable;
+import org.apache.accumulo.core.metadata.RootTable;
+import org.apache.accumulo.monitor.Monitor;
+import org.apache.accumulo.monitor.util.Table;
+import org.apache.accumulo.monitor.util.TableRow;
+import org.apache.accumulo.monitor.util.celltypes.CompactionsType;
+import org.apache.accumulo.monitor.util.celltypes.DurationType;
+import org.apache.accumulo.monitor.util.celltypes.NumberType;
+import org.apache.accumulo.monitor.util.celltypes.TableLinkType;
+import org.apache.accumulo.monitor.util.celltypes.TableStateType;
+import org.apache.accumulo.server.client.HdfsZooInstance;
+import org.apache.accumulo.server.master.state.MetaDataTableScanner;
+import org.apache.accumulo.server.master.state.TabletLocationState;
+import org.apache.accumulo.server.security.SystemCredentials;
+import org.apache.accumulo.server.tables.TableManager;
+import org.apache.accumulo.server.util.TableInfoUtil;
+import org.apache.hadoop.io.Text;
+
+public class TablesServlet extends BasicServlet {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected String getTitle(HttpServletRequest req) {
+ return "Table Status";
+ }
+
+ @Override
+ protected void pageBody(HttpServletRequest req, HttpServletResponse response, StringBuilder sb) throws Exception {
+ Map<String,String> tidToNameMap = Tables.getIdToNameMap(HdfsZooInstance.getInstance());
+ String tableId = req.getParameter("t");
+
+ doProblemsBanner(sb);
+
+ if (tableId == null || tableId.isEmpty() || tidToNameMap.containsKey(tableId) == false) {
+ doTableList(req, sb, tidToNameMap);
+ return;
+ }
+
+ doTableDetails(req, sb, tidToNameMap, tableId);
+ }
+
+ static void doProblemsBanner(StringBuilder sb) {
+ int numProblems = Monitor.getProblemSummary().entrySet().size();
+ if (numProblems > 0)
+ banner(sb, "error", String.format("<a href='/problems'>Table Problems: %d Total</a>", numProblems));
+ }
+
+ static void doTableList(HttpServletRequest req, StringBuilder sb, Map<String,String> tidToNameMap) {
+ Table tableList = new Table("tableList", "Table List");
+ tableList.addSortableColumn("Table Name", new TableLinkType(), null);
+ tableList.addSortableColumn("State", new TableStateType(), null);
+ tableList.addSortableColumn("# Tablets", new NumberType<Integer>(), "Tables are broken down into ranges of rows called tablets.");
+ tableList.addSortableColumn("# Offline<br />Tablets", new NumberType<Integer>(0, 0), "Tablets unavailable for query or ingest. "
+ + "May be a transient condition when tablets are moved for balancing.");
+ tableList.addSortableColumn("Entries", new NumberType<Long>(), "Key/value pairs over each instance, table or tablet.");
+ tableList.addSortableColumn("Entries<br />In Memory", new NumberType<Long>(),
+ "The total number of key/value pairs stored in memory and not yet written to disk");
+ tableList.addSortableColumn("Ingest", new NumberType<Long>(), "The number of Key/Value pairs inserted. Note that deletes are 'inserted'.");
+ tableList.addSortableColumn("Entries<br/>Read", new NumberType<Long>(),
+ "The number of Key/Value pairs read on the server side. Not all key values read may be returned to client because of filtering.");
+ tableList.addSortableColumn("Entries<br/>Returned", new NumberType<Long>(),
+ "The number of Key/Value pairs returned to clients during queries. This is <b>not</b> the number of scans.");
+ tableList.addSortableColumn("Hold Time", new DurationType(0l, 0l),
+ "The amount of time that ingest operations are suspended while waiting for data to be written to disk.");
+ tableList.addSortableColumn("Running<br />Scans", new CompactionsType("scans"),
+ "Information about the scans threads. Shows how many threads are running and how much work is queued for the threads.");
+ tableList.addSortableColumn("Minor<br />Compactions", new CompactionsType("minor"), "Flushing memory to disk is called a \"minor compaction.\" "
+ + "Multiple tablets can be minor compacted simultaneously, but " + "" + "sometimes they must wait for resources to be available. These "
+ + "tablets that are waiting for compaction are \"queued\" and are " + "indicated using parentheses. So <tt> 2 (3)</tt> indicates there are "
+ + "two compactions running and three queued waiting for resources.");
+ tableList.addSortableColumn("Major<br />Compactions", new CompactionsType("major"),
+ "Gathering up many small files and rewriting them as one larger file is called a 'Major Compaction'. "
+ + "Major Compactions are performed as a consequence of new files created from Minor Compactions and Bulk Load operations. "
+ + "They reduce the number of files used during queries.");
+ SortedMap<String,TableInfo> tableStats = new TreeMap<String,TableInfo>();
+
+ if (Monitor.getMmi() != null && Monitor.getMmi().tableMap != null)
+ for (Entry<String,TableInfo> te : Monitor.getMmi().tableMap.entrySet())
+ tableStats.put(Tables.getPrintableTableNameFromId(tidToNameMap, te.getKey()), te.getValue());
+
+ Map<String,Double> compactingByTable = TableInfoUtil.summarizeTableStats(Monitor.getMmi());
+ TableManager tableManager = TableManager.getInstance();
+
+ for (Entry<String,String> tableName_tableId : Tables.getNameToIdMap(HdfsZooInstance.getInstance()).entrySet()) {
+ String tableName = tableName_tableId.getKey();
+ String tableId = tableName_tableId.getValue();
+ TableInfo tableInfo = tableStats.get(tableName);
+ Double holdTime = compactingByTable.get(tableId);
+ if (holdTime == null)
+ holdTime = new Double(0.);
+ TableRow row = tableList.prepareRow();
+ row.add(tableId);
+ row.add(tableManager.getTableState(tableId));
+ row.add(tableInfo == null ? null : tableInfo.tablets);
+ row.add(tableInfo == null ? null : tableInfo.tablets - tableInfo.onlineTablets);
+ row.add(tableInfo == null ? null : tableInfo.recs);
+ row.add(tableInfo == null ? null : tableInfo.recsInMemory);
+ row.add(tableInfo == null ? null : tableInfo.ingestRate);
+ row.add(tableInfo == null ? null : tableInfo.scanRate);
+ row.add(tableInfo == null ? null : tableInfo.queryRate);
+ row.add(holdTime.longValue());
+ row.add(tableInfo);
+ row.add(tableInfo);
+ row.add(tableInfo);
+ tableList.addRow(row);
+ }
+
+ tableList.generate(req, sb);
+ }
+
+ private void doTableDetails(HttpServletRequest req, StringBuilder sb, Map<String,String> tidToNameMap, String tableId) {
+ String displayName = Tables.getPrintableTableNameFromId(tidToNameMap, tableId);
+ Instance instance = HdfsZooInstance.getInstance();
+ TreeSet<String> locs = new TreeSet<String>();
+ if (RootTable.ID.equals(tableId)) {
+ locs.add(instance.getRootTabletLocation());
+ } else {
+ String systemTableName = MetadataTable.ID.equals(tableId) ? RootTable.NAME : MetadataTable.NAME;
+ MetaDataTableScanner scanner = new MetaDataTableScanner(instance, SystemCredentials.get(), new Range(KeyExtent.getMetadataEntry(new Text(tableId),
+ new Text()), KeyExtent.getMetadataEntry(new Text(tableId), null)), systemTableName);
+
+ while (scanner.hasNext()) {
+ TabletLocationState state = scanner.next();
+ if (state.current != null) {
+ try {
+ locs.add(state.current.hostPort());
+ } catch (Exception ex) {
+ log.error(ex, ex);
+ }
+ }
+ }
+ scanner.close();
+ }
+
+ log.debug("Locs: " + locs);
+
+ List<TabletServerStatus> tservers = new ArrayList<TabletServerStatus>();
+ if (Monitor.getMmi() != null) {
+ for (TabletServerStatus tss : Monitor.getMmi().tServerInfo) {
+ try {
+ log.debug("tss: " + tss.name);
+ if (tss.name != null && locs.contains(tss.name))
+ tservers.add(tss);
+ } catch (Exception ex) {
+ log.error(ex, ex);
+ }
+ }
+ }
+
+ Table tableDetails = new Table("participatingTServers", "Participating Tablet Servers");
+ tableDetails.setSubCaption(displayName);
+ TServersServlet.doTserverList(req, sb, tservers, tableId, tableDetails);
+ }
+}
http://git-wip-us.apache.org/repos/asf/accumulo/blob/598821cd/server/monitor/src/main/java/org/apache/accumulo/monitor/servlets/VisServlet.java
----------------------------------------------------------------------
diff --git a/server/monitor/src/main/java/org/apache/accumulo/monitor/servlets/VisServlet.java b/server/monitor/src/main/java/org/apache/accumulo/monitor/servlets/VisServlet.java
new file mode 100644
index 0000000..5d2d2db
--- /dev/null
+++ b/server/monitor/src/main/java/org/apache/accumulo/monitor/servlets/VisServlet.java
@@ -0,0 +1,236 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.accumulo.monitor.servlets;
+
+import java.io.IOException;
+import java.lang.management.ManagementFactory;
+import java.util.ArrayList;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.accumulo.core.conf.Property;
+import org.apache.accumulo.core.master.thrift.TabletServerStatus;
+import org.apache.accumulo.monitor.Monitor;
+
+public class VisServlet extends BasicServlet {
+ private static final int concurrentScans = Monitor.getSystemConfiguration().getCount(Property.TSERV_READ_AHEAD_MAXCONCURRENT);
+
+ private static final long serialVersionUID = 1L;
+
+ public enum StatType {
+ osload(ManagementFactory.getOperatingSystemMXBean().getAvailableProcessors(), true, 100, "OS Load"),
+ ingest(1000, true, 1, "Ingest Entries"),
+ query(10000, true, 1, "Scan Entries"),
+ ingestMB(10, true, 10, "Ingest MB"),
+ queryMB(5, true, 10, "Scan MB"),
+ scans(concurrentScans * 2, false, 1, "Running Scans"),
+ scansessions(50, true, 10, "Scan Sessions"),
+ holdtime(60000, false, 1, "Hold Time"),
+ allavg(1, false, 100, "Overall Avg", true),
+ allmax(1, false, 100, "Overall Max", true);
+
+ private int max;
+ private boolean adjustMax;
+ private float significance;
+ private String description;
+ private boolean derived;
+
+ /**
+ * @param max
+ * initial estimate of largest possible value for this stat
+ * @param adjustMax
+ * indicates whether max should be adjusted based on observed values
+ * @param significance
+ * values will be converted by floor(significance*value)/significance
+ * @param description
+ * as appears in selection box
+ */
+ private StatType(int max, boolean adjustMax, float significance, String description) {
+ this(max, adjustMax, significance, description, false);
+ }
+
+ private StatType(int max, boolean adjustMax, float significance, String description, boolean derived) {
+ this.max = max;
+ this.adjustMax = adjustMax;
+ this.significance = significance;
+ this.description = description;
+ this.derived = derived;
+ }
+
+ public int getMax() {
+ return max;
+ }
+
+ public boolean getAdjustMax() {
+ return adjustMax;
+ }
+
+ public float getSignificance() {
+ return significance;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public boolean isDerived() {
+ return derived;
+ }
+
+ public static int numDerived() {
+ int count = 0;
+ for (StatType st : StatType.values())
+ if (st.isDerived())
+ count++;
+ return count;
+ }
+ }
+
+ public static class VisualizationConfig {
+ boolean useCircles = true;
+ StatType motion = StatType.allmax;
+ StatType color = StatType.allavg;
+ int spacing = 40;
+ String url;
+ }
+
+ @Override
+ protected String getTitle(HttpServletRequest req) {
+ return "Server Activity";
+ }
+
+ @Override
+ protected void pageBody(HttpServletRequest req, HttpServletResponse response, StringBuilder sb) throws IOException {
+ StringBuffer urlsb = req.getRequestURL();
+ urlsb.setLength(urlsb.lastIndexOf("/") + 1);
+ VisualizationConfig cfg = new VisualizationConfig();
+ cfg.url = urlsb.toString();
+
+ String s = req.getParameter("shape");
+ if (s != null && (s.equals("square") || s.equals("squares"))) {
+ cfg.useCircles = false;
+ }
+
+ s = req.getParameter("motion");
+ if (s != null) {
+ try {
+ cfg.motion = StatType.valueOf(s);
+ } catch (Exception e) {}
+ }
+
+ s = req.getParameter("color");
+ if (s != null) {
+ try {
+ cfg.color = StatType.valueOf(s);
+ } catch (Exception e) {}
+ }
+
+ String size = req.getParameter("size");
+ if (size != null) {
+ if (size.equals("10"))
+ cfg.spacing = 10;
+ else if (size.equals("20"))
+ cfg.spacing = 20;
+ else if (size.equals("80"))
+ cfg.spacing = 80;
+ }
+
+ ArrayList<TabletServerStatus> tservers = new ArrayList<TabletServerStatus>();
+ if (Monitor.getMmi() != null)
+ tservers.addAll(Monitor.getMmi().tServerInfo);
+
+ if (tservers.size() == 0)
+ return;
+
+ int width = (int) Math.ceil(Math.sqrt(tservers.size())) * cfg.spacing;
+ int height = (int) Math.ceil(tservers.size() / width) * cfg.spacing;
+ doSettings(sb, cfg, width < 640 ? 640 : width, height < 640 ? 640 : height);
+ doScript(sb, cfg, tservers);
+ }
+
+ private void doSettings(StringBuilder sb, VisualizationConfig cfg, int width, int height) {
+ sb.append("<div class='left'>\n");
+ sb.append("<div id='parameters' class='nowrap'>\n");
+ // shape select box
+ sb.append("<span class='viscontrol'>Shape: <select id='shape' onchange='setShape(this)'><option>Circles</option><option")
+ .append(!cfg.useCircles ? " selected='true'" : "").append(">Squares</option></select></span>\n");
+ // size select box
+ sb.append("  <span class='viscontrol'>Size: <select id='size' onchange='setSize(this)'><option")
+ .append(cfg.spacing == 10 ? " selected='true'" : "").append(">10</option><option").append(cfg.spacing == 20 ? " selected='true'" : "")
+ .append(">20</option><option").append(cfg.spacing == 40 ? " selected='true'" : "").append(">40</option><option")
+ .append(cfg.spacing == 80 ? " selected='true'" : "").append(">80</option></select></span>\n");
+ // motion select box
+ sb.append("  <span class='viscontrol'>Motion: <select id='motion' onchange='setMotion(this)'>");
+ sb.append("<option selected='true'></option>");
+ addOptions(sb, null);
+ sb.append("</select></span>\n");
+ // color select box
+ sb.append("  <span class='viscontrol'>Color: <select id='color' onchange='setColor(this)'>");
+ addOptions(sb, cfg.color);
+ sb.append("</select></span>\n");
+ sb.append("  <span class='viscontrol'>(hover for info, click for details)</span>");
+ sb.append("</div>\n\n");
+ sb.append("<div id='hoverable'>\n");
+ // floating info box
+ sb.append("<div id='vishoverinfo'></div>\n\n");
+ // canvas
+ sb.append("<br><canvas id='visCanvas' width='").append(width).append("' height='").append(height).append("'>Browser does not support canvas.</canvas>\n\n");
+ sb.append("</div>\n");
+ sb.append("</div>\n\n");
+ }
+
+ private void addOptions(StringBuilder sb, StatType selectedStatType) {
+ for (StatType st : StatType.values()) {
+ sb.append("<option").append(st.equals(selectedStatType) ? " selected='true'>" : ">").append(st.getDescription()).append("</option>");
+ }
+ }
+
+ private void doScript(StringBuilder sb, VisualizationConfig cfg, ArrayList<TabletServerStatus> tservers) {
+ // initialization of some javascript variables
+ sb.append("<script type='text/javascript'>\n");
+ sb.append("var numCores = " + ManagementFactory.getOperatingSystemMXBean().getAvailableProcessors() + ";\n");
+ sb.append("var jsonurl = '" + cfg.url + "json';\n");
+ sb.append("var visurl = '" + cfg.url + "vis';\n");
+ sb.append("var serverurl = '" + cfg.url + "tservers?s=';\n\n");
+ sb.append("// observable stats that can be connected to motion or color\n");
+ sb.append("var statNames = {");
+ for (StatType st : StatType.values())
+ sb.append("'").append(st).append("': ").append(st.derived).append(",");
+ sb.setLength(sb.length() - 1);
+ sb.append("};\n");
+ sb.append("var maxStatValues = {");
+ for (StatType st : StatType.values())
+ sb.append("'").append(st).append("': ").append(st.getMax()).append(", ");
+ sb.setLength(sb.length() - 2);
+ sb.append("}; // initial values that are system-dependent may increase based on observed values\n");
+ sb.append("var adjustMax = {");
+ for (StatType st : StatType.values())
+ sb.append("'").append(st).append("': ").append(st.getAdjustMax()).append(", ");
+ sb.setLength(sb.length() - 2);
+ sb.append("}; // whether to allow increases in the max based on observed values\n");
+ sb.append("var significance = {");
+ for (StatType st : StatType.values())
+ sb.append("'").append(st).append("': ").append(st.getSignificance()).append(", ");
+ sb.setLength(sb.length() - 2);
+ sb.append("}; // values will be converted by floor(this*value)/this\n");
+ sb.append("var numNormalStats = ").append(StatType.values().length - StatType.numDerived()).append(";\n");
+ sb.append("</script>\n");
+
+ sb.append("<script src='web/vis.js' type='text/javascript'></script>");
+ }
+}
http://git-wip-us.apache.org/repos/asf/accumulo/blob/598821cd/server/monitor/src/main/java/org/apache/accumulo/monitor/servlets/XMLServlet.java
----------------------------------------------------------------------
diff --git a/server/monitor/src/main/java/org/apache/accumulo/monitor/servlets/XMLServlet.java b/server/monitor/src/main/java/org/apache/accumulo/monitor/servlets/XMLServlet.java
new file mode 100644
index 0000000..0cbf957
--- /dev/null
+++ b/server/monitor/src/main/java/org/apache/accumulo/monitor/servlets/XMLServlet.java
@@ -0,0 +1,179 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.accumulo.monitor.servlets;
+
+import java.util.Map.Entry;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.accumulo.core.Constants;
+import org.apache.accumulo.core.client.Instance;
+import org.apache.accumulo.core.client.impl.Tables;
+import org.apache.accumulo.core.master.thrift.Compacting;
+import org.apache.accumulo.core.master.thrift.DeadServer;
+import org.apache.accumulo.core.master.thrift.TableInfo;
+import org.apache.accumulo.core.master.thrift.TabletServerStatus;
+import org.apache.accumulo.monitor.Monitor;
+import org.apache.accumulo.monitor.util.celltypes.TServerLinkType;
+import org.apache.accumulo.server.client.HdfsZooInstance;
+import org.apache.accumulo.server.master.state.TabletServerState;
+import org.apache.accumulo.server.util.TableInfoUtil;
+
+public class XMLServlet extends BasicServlet {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected String getTitle(HttpServletRequest req) {
+ return "XML Report";
+ }
+
+ @Override
+ protected void pageStart(HttpServletRequest req, HttpServletResponse resp, StringBuilder sb) {
+ resp.setContentType("text/xml;charset=" + Constants.UTF8.name());
+ sb.append("<?xml version=\"1.0\" encoding=\"" + Constants.UTF8.name() + "\"?>\n");
+ sb.append("<stats>\n");
+ }
+
+ @Override
+ protected void pageBody(HttpServletRequest req, HttpServletResponse resp, StringBuilder sb) {
+ double totalIngest = 0.;
+ double totalQuery = 0.;
+ double disk = 0.0;
+ long totalEntries = 0L;
+
+ sb.append("\n<servers>\n");
+ if (Monitor.getMmi() == null || Monitor.getMmi().tableMap == null) {
+ sb.append("</servers>\n");
+ return;
+ }
+ SortedMap<String,TableInfo> tableStats = new TreeMap<String,TableInfo>(Monitor.getMmi().tableMap);
+
+ for (TabletServerStatus status : Monitor.getMmi().tServerInfo) {
+
+ sb.append("\n<server id='").append(status.name).append("'>\n");
+ sb.append("<hostname>").append(TServerLinkType.displayName(status.name)).append("</hostname>");
+ sb.append("<lastContact>").append(System.currentTimeMillis() - status.lastContact).append("</lastContact>\n");
+ sb.append("<osload>").append(status.osLoad).append("</osload>\n");
+
+ TableInfo summary = TableInfoUtil.summarizeTableStats(status);
+ sb.append("<compactions>\n");
+ sb.append("<major>").append("<running>").append(summary.majors.running).append("</running>").append("<queued>").append(summary.majors.queued)
+ .append("</queued>").append("</major>\n");
+ sb.append("<minor>").append("<running>").append(summary.minors.running).append("</running>").append("<queued>").append(summary.minors.queued)
+ .append("</queued>").append("</minor>\n");
+ sb.append("</compactions>\n");
+
+ sb.append("<tablets>").append(summary.tablets).append("</tablets>\n");
+
+ sb.append("<ingest>").append(summary.ingestRate).append("</ingest>\n");
+ sb.append("<query>").append(summary.queryRate).append("</query>\n");
+ sb.append("<ingestMB>").append(summary.ingestByteRate / 1000000.0).append("</ingestMB>\n");
+ sb.append("<queryMB>").append(summary.queryByteRate / 1000000.0).append("</queryMB>\n");
+ sb.append("<scans>").append(summary.scans.running + summary.scans.queued).append("</scans>");
+ sb.append("<scansessions>").append(Monitor.getLookupRate()).append("</scansessions>\n");
+ sb.append("<holdtime>").append(status.holdTime).append("</holdtime>\n");
+ totalIngest += summary.ingestRate;
+ totalQuery += summary.queryRate;
+ totalEntries += summary.recs;
+ sb.append("</server>\n");
+ }
+ sb.append("\n</servers>\n");
+
+ sb.append("\n<masterGoalState>" + Monitor.getMmi().goalState + "</masterGoalState>\n");
+ sb.append("\n<masterState>" + Monitor.getMmi().state + "</masterState>\n");
+
+ sb.append("\n<badTabletServers>\n");
+ for (Entry<String,Byte> entry : Monitor.getMmi().badTServers.entrySet()) {
+ sb.append(String.format("<badTabletServer id='%s' status='%s'/>\n", entry.getKey(), TabletServerState.getStateById(entry.getValue())));
+ }
+ sb.append("\n</badTabletServers>\n");
+
+ sb.append("\n<tabletServersShuttingDown>\n");
+ for (String server : Monitor.getMmi().serversShuttingDown) {
+ sb.append(String.format("<server id='%s'/>\n", server));
+ }
+ sb.append("\n</tabletServersShuttingDown>\n");
+
+ sb.append(String.format("\n<unassignedTablets>%d</unassignedTablets>\n", Monitor.getMmi().unassignedTablets));
+
+ sb.append("\n<deadTabletServers>\n");
+ for (DeadServer dead : Monitor.getMmi().deadTabletServers) {
+ sb.append(String.format("<deadTabletServer id='%s' lastChange='%d' status='%s'/>\n", dead.server, dead.lastStatus, dead.status));
+ }
+ sb.append("\n</deadTabletServers>\n");
+
+ sb.append("\n<deadLoggers>\n");
+ for (DeadServer dead : Monitor.getMmi().deadTabletServers) {
+ sb.append(String.format("<deadLogger id='%s' lastChange='%d' status='%s'/>\n", dead.server, dead.lastStatus, dead.status));
+ }
+ sb.append("\n</deadLoggers>\n");
+
+ sb.append("\n<tables>\n");
+ Instance instance = HdfsZooInstance.getInstance();
+ for (Entry<String,TableInfo> entry : tableStats.entrySet()) {
+ TableInfo tableInfo = entry.getValue();
+
+ sb.append("\n<table>\n");
+ String tableId = entry.getKey();
+ String tableName = "unknown";
+ String tableState = "unknown";
+ try {
+ tableName = Tables.getTableName(instance, tableId);
+ tableState = Tables.getTableState(instance, tableId).toString();
+ } catch (Exception ex) {
+ log.warn(ex, ex);
+ }
+ sb.append("<tablename>").append(tableName).append("</tablename>\n");
+ sb.append("<tableId>").append(tableId).append("</tableId>\n");
+ sb.append("<tableState>").append(tableState).append("</tableState>\n");
+ sb.append("<tablets>").append(tableInfo.tablets).append("</tablets>\n");
+ sb.append("<onlineTablets>").append(tableInfo.onlineTablets).append("</onlineTablets>\n");
+ sb.append("<recs>").append(tableInfo.recs).append("</recs>\n");
+ sb.append("<recsInMemory>").append(tableInfo.recsInMemory).append("</recsInMemory>\n");
+ sb.append("<ingest>").append(tableInfo.ingestRate).append("</ingest>\n");
+ sb.append("<ingestByteRate>").append(tableInfo.ingestByteRate).append("</ingestByteRate>\n");
+ sb.append("<query>").append(tableInfo.queryRate).append("</query>\n");
+ sb.append("<queryByteRate>").append(tableInfo.queryRate).append("</queryByteRate>\n");
+ int running = 0;
+ int queued = 0;
+ Compacting compacting = entry.getValue().majors;
+ if (compacting != null) {
+ running = compacting.running;
+ queued = compacting.queued;
+ }
+ sb.append("<majorCompactions>").append("<running>").append(running).append("</running>").append("<queued>").append(queued).append("</queued>")
+ .append("</majorCompactions>\n");
+ sb.append("</table>\n");
+ }
+ sb.append("\n</tables>\n");
+
+ sb.append("\n<totals>\n");
+ sb.append("<ingestrate>").append(totalIngest).append("</ingestrate>\n");
+ sb.append("<queryrate>").append(totalQuery).append("</queryrate>\n");
+ sb.append("<diskrate>").append(disk).append("</diskrate>\n");
+ sb.append("<numentries>").append(totalEntries).append("</numentries>\n");
+ sb.append("</totals>\n");
+ }
+
+ @Override
+ protected void pageEnd(HttpServletRequest req, HttpServletResponse resp, StringBuilder sb) {
+ sb.append("\n</stats>\n");
+ }
+}
http://git-wip-us.apache.org/repos/asf/accumulo/blob/598821cd/server/monitor/src/main/java/org/apache/accumulo/monitor/servlets/trace/Basic.java
----------------------------------------------------------------------
diff --git a/server/monitor/src/main/java/org/apache/accumulo/monitor/servlets/trace/Basic.java b/server/monitor/src/main/java/org/apache/accumulo/monitor/servlets/trace/Basic.java
new file mode 100644
index 0000000..d85efa5
--- /dev/null
+++ b/server/monitor/src/main/java/org/apache/accumulo/monitor/servlets/trace/Basic.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.accumulo.monitor.servlets.trace;
+
+import java.util.Date;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.accumulo.core.client.AccumuloException;
+import org.apache.accumulo.core.client.AccumuloSecurityException;
+import org.apache.accumulo.core.client.Connector;
+import org.apache.accumulo.core.client.Scanner;
+import org.apache.accumulo.core.client.TableNotFoundException;
+import org.apache.accumulo.core.client.security.tokens.AuthenticationToken;
+import org.apache.accumulo.core.client.security.tokens.AuthenticationToken.Properties;
+import org.apache.accumulo.core.client.security.tokens.PasswordToken;
+import org.apache.accumulo.core.conf.AccumuloConfiguration;
+import org.apache.accumulo.core.conf.Property;
+import org.apache.accumulo.core.trace.TraceFormatter;
+import org.apache.accumulo.monitor.Monitor;
+import org.apache.accumulo.monitor.servlets.BasicServlet;
+import org.apache.accumulo.server.client.HdfsZooInstance;
+
+abstract class Basic extends BasicServlet {
+
+ private static final long serialVersionUID = 1L;
+
+ public static String getStringParameter(HttpServletRequest req, String name, String defaultValue) {
+ String result = req.getParameter(name);
+ if (result == null) {
+ return defaultValue;
+ }
+ return result;
+ }
+
+ public static int getIntParameter(HttpServletRequest req, String name, int defaultMinutes) {
+ String valueString = req.getParameter(name);
+ if (valueString == null)
+ return defaultMinutes;
+ int result = 0;
+ try {
+ result = Integer.parseInt(valueString);
+ } catch (NumberFormatException ex) {
+ return defaultMinutes;
+ }
+ return result;
+ }
+
+ public static String dateString(long millis) {
+ return TraceFormatter.formatDate(new Date(millis));
+ }
+
+ protected Scanner getScanner(StringBuilder sb) throws AccumuloException, AccumuloSecurityException {
+ AccumuloConfiguration conf = Monitor.getSystemConfiguration();
+ String principal = conf.get(Property.TRACE_USER);
+ AuthenticationToken at;
+ Map<String,String> loginMap = conf.getAllPropertiesWithPrefix(Property.TRACE_TOKEN_PROPERTY_PREFIX);
+ if (loginMap.isEmpty()) {
+ Property p = Property.TRACE_PASSWORD;
+ at = new PasswordToken(conf.get(p).getBytes());
+ } else {
+ Properties props = new Properties();
+ int prefixLength = Property.TRACE_TOKEN_PROPERTY_PREFIX.getKey().length() + 1;
+ for (Entry<String,String> entry : loginMap.entrySet()) {
+ props.put(entry.getKey().substring(prefixLength), entry.getValue());
+ }
+
+ AuthenticationToken token = Property.createInstanceFromPropertyName(conf, Property.TRACE_TOKEN_TYPE, AuthenticationToken.class, new PasswordToken());
+ token.init(props);
+ at = token;
+ }
+
+ String table = conf.get(Property.TRACE_TABLE);
+ try {
+ Connector conn = HdfsZooInstance.getInstance().getConnector(principal, at);
+ if (!conn.tableOperations().exists(table)) {
+ return new NullScanner();
+ }
+ Scanner scanner = conn.createScanner(table, conn.securityOperations().getUserAuthorizations(principal));
+ return scanner;
+ } catch (AccumuloSecurityException ex) {
+ sb.append("<h2>Unable to read trace table: check trace username and password configuration.</h2>\n");
+ return null;
+ } catch (TableNotFoundException ex) {
+ return new NullScanner();
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/accumulo/blob/598821cd/server/monitor/src/main/java/org/apache/accumulo/monitor/servlets/trace/ListType.java
----------------------------------------------------------------------
diff --git a/server/monitor/src/main/java/org/apache/accumulo/monitor/servlets/trace/ListType.java b/server/monitor/src/main/java/org/apache/accumulo/monitor/servlets/trace/ListType.java
new file mode 100644
index 0000000..e29c5d7
--- /dev/null
+++ b/server/monitor/src/main/java/org/apache/accumulo/monitor/servlets/trace/ListType.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.accumulo.monitor.servlets.trace;
+
+import java.util.Map.Entry;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.accumulo.core.client.Scanner;
+import org.apache.accumulo.core.data.Key;
+import org.apache.accumulo.core.data.Range;
+import org.apache.accumulo.core.data.Value;
+import org.apache.accumulo.core.trace.TraceFormatter;
+import org.apache.accumulo.monitor.util.Table;
+import org.apache.accumulo.monitor.util.celltypes.DurationType;
+import org.apache.accumulo.monitor.util.celltypes.StringType;
+import org.apache.accumulo.trace.thrift.RemoteSpan;
+import org.apache.hadoop.io.Text;
+
+public class ListType extends Basic {
+
+ private static final long serialVersionUID = 1L;
+
+ String getType(HttpServletRequest req) {
+ return getStringParameter(req, "type", "<Unknown>");
+ }
+
+ int getMinutes(HttpServletRequest req) {
+ return getIntParameter(req, "minutes", Summary.DEFAULT_MINUTES);
+ }
+
+ @Override
+ public void pageBody(HttpServletRequest req, HttpServletResponse resp, StringBuilder sb) throws Exception {
+ String type = getType(req);
+ int minutes = getMinutes(req);
+ long endTime = System.currentTimeMillis();
+ long startTime = endTime - minutes * 60 * 1000;
+ Scanner scanner = getScanner(sb);
+ if (scanner == null) {
+ return;
+ }
+ Range range = new Range(new Text("start:" + Long.toHexString(startTime)), new Text("start:" + Long.toHexString(endTime)));
+ scanner.setRange(range);
+ Table trace = new Table("trace", "Traces for " + getType(req));
+ trace.addSortableColumn("Start", new ShowTraceLinkType(), "Start Time");
+ trace.addSortableColumn("ms", new DurationType(), "Span time");
+ trace.addUnsortableColumn("Source", new StringType<String>(), "Service and location");
+ for (Entry<Key,Value> entry : scanner) {
+ RemoteSpan span = TraceFormatter.getRemoteSpan(entry);
+ if (span.description.equals(type)) {
+ trace.addRow(span, new Long(span.stop - span.start), span.svc + ":" + span.sender);
+ }
+ }
+ trace.generate(req, sb);
+ }
+
+ @Override
+ public String getTitle(HttpServletRequest req) {
+ return "Traces for " + getType(req) + " for the last " + getMinutes(req) + " minutes";
+ }
+}
http://git-wip-us.apache.org/repos/asf/accumulo/blob/598821cd/server/monitor/src/main/java/org/apache/accumulo/monitor/servlets/trace/NullKeyValueIterator.java
----------------------------------------------------------------------
diff --git a/server/monitor/src/main/java/org/apache/accumulo/monitor/servlets/trace/NullKeyValueIterator.java b/server/monitor/src/main/java/org/apache/accumulo/monitor/servlets/trace/NullKeyValueIterator.java
new file mode 100644
index 0000000..26cfb07
--- /dev/null
+++ b/server/monitor/src/main/java/org/apache/accumulo/monitor/servlets/trace/NullKeyValueIterator.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.accumulo.monitor.servlets.trace;
+
+import java.util.Iterator;
+import java.util.Map.Entry;
+
+import org.apache.accumulo.core.data.Key;
+import org.apache.accumulo.core.data.Value;
+
+public class NullKeyValueIterator implements Iterator<Entry<Key,Value>> {
+
+ @Override
+ public boolean hasNext() {
+ return false;
+ }
+
+ @Override
+ public Entry<Key,Value> next() {
+ return null;
+ }
+
+ @Override
+ public void remove() {}
+}
http://git-wip-us.apache.org/repos/asf/accumulo/blob/598821cd/server/monitor/src/main/java/org/apache/accumulo/monitor/servlets/trace/NullScanner.java
----------------------------------------------------------------------
diff --git a/server/monitor/src/main/java/org/apache/accumulo/monitor/servlets/trace/NullScanner.java b/server/monitor/src/main/java/org/apache/accumulo/monitor/servlets/trace/NullScanner.java
new file mode 100644
index 0000000..bf35557
--- /dev/null
+++ b/server/monitor/src/main/java/org/apache/accumulo/monitor/servlets/trace/NullScanner.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.accumulo.monitor.servlets.trace;
+
+import java.util.Iterator;
+import java.util.Map.Entry;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.accumulo.core.client.IteratorSetting;
+import org.apache.accumulo.core.client.Scanner;
+import org.apache.accumulo.core.data.Key;
+import org.apache.accumulo.core.data.Range;
+import org.apache.accumulo.core.data.Value;
+import org.apache.hadoop.io.Text;
+
+public class NullScanner implements Scanner {
+
+ @Override
+ public void addScanIterator(IteratorSetting cfg) {}
+
+ @Override
+ public void updateScanIteratorOption(String iteratorName, String key, String value) {}
+
+ @Override
+ public void fetchColumnFamily(Text col) {}
+
+ @Override
+ public void fetchColumn(Text colFam, Text colQual) {}
+
+ @Override
+ public void clearColumns() {}
+
+ @Override
+ public void clearScanIterators() {}
+
+ @Deprecated
+ @Override
+ public void setTimeOut(int timeOut) {}
+
+ @Deprecated
+ @Override
+ public int getTimeOut() {
+ return 0;
+ }
+
+ @Override
+ public void setRange(Range range) {}
+
+ @Override
+ public Range getRange() {
+ return null;
+ }
+
+ @Override
+ public void setBatchSize(int size) {
+
+ }
+
+ @Override
+ public int getBatchSize() {
+ return 0;
+ }
+
+ @Override
+ public void enableIsolation() {
+
+ }
+
+ @Override
+ public void disableIsolation() {
+
+ }
+
+ @Override
+ public Iterator<Entry<Key,Value>> iterator() {
+ return new NullKeyValueIterator();
+ }
+
+ @Override
+ public void removeScanIterator(String iteratorName) {}
+
+ @Override
+ public void setTimeout(long timeOut, TimeUnit timeUnit) {}
+
+ @Override
+ public long getTimeout(TimeUnit timeUnit) {
+ return 0;
+ }
+
+ @Override
+ public void close() {}
+
+ @Override
+ public long getReadaheadThreshold() {
+ return 0l;
+ }
+
+ @Override
+ public void setReadaheadThreshold(long batches) {
+
+ }
+}
http://git-wip-us.apache.org/repos/asf/accumulo/blob/598821cd/server/monitor/src/main/java/org/apache/accumulo/monitor/servlets/trace/ShowTrace.java
----------------------------------------------------------------------
diff --git a/server/monitor/src/main/java/org/apache/accumulo/monitor/servlets/trace/ShowTrace.java b/server/monitor/src/main/java/org/apache/accumulo/monitor/servlets/trace/ShowTrace.java
new file mode 100644
index 0000000..a476201
--- /dev/null
+++ b/server/monitor/src/main/java/org/apache/accumulo/monitor/servlets/trace/ShowTrace.java
@@ -0,0 +1,159 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.accumulo.monitor.servlets.trace;
+
+import static java.lang.Math.min;
+
+import java.util.Collection;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.accumulo.core.client.Scanner;
+import org.apache.accumulo.core.data.Key;
+import org.apache.accumulo.core.data.Range;
+import org.apache.accumulo.core.data.Value;
+import org.apache.accumulo.core.trace.SpanTree;
+import org.apache.accumulo.core.trace.SpanTreeVisitor;
+import org.apache.accumulo.core.trace.TraceDump;
+import org.apache.accumulo.core.trace.TraceFormatter;
+import org.apache.accumulo.monitor.servlets.BasicServlet;
+import org.apache.accumulo.trace.thrift.RemoteSpan;
+import org.apache.hadoop.io.Text;
+
+public class ShowTrace extends Basic {
+
+ private static final long serialVersionUID = 1L;
+ private static final String checkboxIdSuffix = "_checkbox";
+ private static final String pageLoadFunctionName = "pageload";
+
+ String getTraceId(HttpServletRequest req) {
+ return getStringParameter(req, "id", null);
+ }
+
+ @Override
+ public String getTitle(HttpServletRequest req) {
+ String id = getTraceId(req);
+ if (id == null)
+ return "No trace id specified";
+ return "Trace ID " + id;
+ }
+
+ @Override
+ public void pageBody(HttpServletRequest req, HttpServletResponse resp, final StringBuilder sb) throws Exception {
+ String id = getTraceId(req);
+ if (id == null) {
+ return;
+ }
+ Scanner scanner = getScanner(sb);
+ if (scanner == null) {
+ return;
+ }
+ Range range = new Range(new Text(id));
+ scanner.setRange(range);
+ SpanTree tree = new SpanTree();
+ long start = Long.MAX_VALUE;
+ for (Entry<Key,Value> entry : scanner) {
+ RemoteSpan span = TraceFormatter.getRemoteSpan(entry);
+ tree.addNode(span);
+ start = min(start, span.start);
+ }
+ sb.append("<style>\n");
+ sb.append(" td.right { text-align: right }\n");
+ sb.append(" table.indent { position: relative; left: 10% }\n");
+ sb.append(" td.left { text-align: left }\n");
+ sb.append("</style>\n");
+ sb.append("<script language='javascript'>\n");
+ sb.append("function toggle(id) {\n");
+ sb.append(" var elt = document.getElementById(id);\n");
+ sb.append(" if (elt.style.display=='none') {\n");
+ sb.append(" elt.style.display='table-row';\n");
+ sb.append(" } else { \n");
+ sb.append(" elt.style.display='none';\n ");
+ sb.append(" }\n");
+ sb.append("}\n");
+
+ sb.append("function ").append(pageLoadFunctionName).append("() {\n");
+ sb.append(" var checkboxes = document.getElementsByTagName('input');\n");
+ sb.append(" for (var i = 0; i < checkboxes.length; i++) {\n");
+ sb.append(" if (checkboxes[i].checked) {\n");
+ sb.append(" var idSuffixOffset = checkboxes[i].id.indexOf('").append(checkboxIdSuffix).append("');\n");
+ sb.append(" var id = checkboxes[i].id.substring(0, idSuffixOffset);\n");
+ sb.append(" document.getElementById(id).style.display='table-row';\n");
+ sb.append(" }\n");
+ sb.append(" }\n");
+ sb.append("}\n");
+
+ sb.append("</script>\n");
+ sb.append("<div>");
+ sb.append("<table><caption>");
+ sb.append(String.format("<span class='table-caption'>Trace %s started at<br>%s</span></caption>", id, dateString(start)));
+ sb.append("<tr><th>Time</th><th>Start</th><th>Service@Location</th><th>Name</th><th>Addl Data</th></tr>");
+
+ final long finalStart = start;
+ Set<Long> visited = tree.visit(new SpanTreeVisitor() {
+ @Override
+ public void visit(int level, RemoteSpan parent, RemoteSpan node, Collection<RemoteSpan> children) {
+ sb.append("<tr>\n");
+ sb.append(String.format("<td class='right'>%d+</td><td class='left'>%d</td>%n", node.stop - node.start, node.start - finalStart));
+ sb.append(String.format("<td style='text-indent: %dpx'>%s@%s</td>%n", level * 5, node.svc, node.sender));
+ sb.append("<td>" + node.description + "</td>");
+ boolean hasData = node.data != null && !node.data.isEmpty();
+ if (hasData) {
+ String hexSpanId = Long.toHexString(node.spanId);
+ sb.append("<td><input type='checkbox' id=\"");
+ sb.append(hexSpanId);
+ sb.append(checkboxIdSuffix);
+ sb.append("\" onclick='toggle(\"" + Long.toHexString(node.spanId) + "\")'></td>\n");
+ } else {
+ sb.append("<td></td>\n");
+ }
+ sb.append("</tr>\n");
+ sb.append("<tr id='" + Long.toHexString(node.spanId) + "' style='display:none'>");
+ sb.append("<td colspan='5'>\n");
+ if (hasData) {
+ sb.append(" <table class='indent,noborder'>\n");
+ for (Entry<String,String> entry : node.data.entrySet()) {
+ sb.append(" <tr><td>" + BasicServlet.sanitize(entry.getKey()) + "</td>");
+ sb.append("<td>" + BasicServlet.sanitize(entry.getValue()) + "</td></tr>\n");
+ }
+ sb.append(" </table>");
+ }
+ sb.append("</td>\n");
+ sb.append("</tr>\n");
+ }
+ });
+ tree.nodes.keySet().removeAll(visited);
+ if (!tree.nodes.isEmpty()) {
+ sb.append("<span type='warn'>Warning: the following spans are not rooted!</span>\n");
+ sb.append("<ul>\n");
+ for (RemoteSpan span : TraceDump.sortByStart(tree.nodes.values())) {
+ sb.append(String.format("<li>%s %s %s</li>\n", Long.toHexString(span.spanId), Long.toHexString(span.parentId), span.description));
+ }
+ sb.append("</ul>\n");
+ }
+ sb.append("</table>\n");
+ sb.append("</div>\n");
+ }
+
+ @Override
+ protected String getBodyAttributes() {
+ return " onload=\"" + pageLoadFunctionName + "()\" ";
+ }
+}
http://git-wip-us.apache.org/repos/asf/accumulo/blob/598821cd/server/monitor/src/main/java/org/apache/accumulo/monitor/servlets/trace/ShowTraceLinkType.java
----------------------------------------------------------------------
diff --git a/server/monitor/src/main/java/org/apache/accumulo/monitor/servlets/trace/ShowTraceLinkType.java b/server/monitor/src/main/java/org/apache/accumulo/monitor/servlets/trace/ShowTraceLinkType.java
new file mode 100644
index 0000000..98b7d6e
--- /dev/null
+++ b/server/monitor/src/main/java/org/apache/accumulo/monitor/servlets/trace/ShowTraceLinkType.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.accumulo.monitor.servlets.trace;
+
+import java.util.Date;
+
+import org.apache.accumulo.core.trace.TraceFormatter;
+import org.apache.accumulo.monitor.util.celltypes.StringType;
+import org.apache.accumulo.trace.thrift.RemoteSpan;
+
+/**
+ *
+ */
+public class ShowTraceLinkType extends StringType<RemoteSpan> {
+ @Override
+ public String format(Object obj) {
+ if (obj == null)
+ return "-";
+ RemoteSpan span = (RemoteSpan) obj;
+ return String.format("<a href='/trace/show?id=%s'>%s</a>", Long.toHexString(span.traceId), TraceFormatter.formatDate(new Date(span.start)));
+ }
+
+ @Override
+ public int compare(RemoteSpan o1, RemoteSpan o2) {
+ if (o1 == null && o2 == null)
+ return 0;
+ else if (o1 == null)
+ return -1;
+ else if (o2 == null)
+ return 1;
+ return o1.start < o2.start ? -1 : (o1.start == o2.start ? 0 : 1);
+ }
+}
http://git-wip-us.apache.org/repos/asf/accumulo/blob/598821cd/server/monitor/src/main/java/org/apache/accumulo/monitor/servlets/trace/Summary.java
----------------------------------------------------------------------
diff --git a/server/monitor/src/main/java/org/apache/accumulo/monitor/servlets/trace/Summary.java b/server/monitor/src/main/java/org/apache/accumulo/monitor/servlets/trace/Summary.java
new file mode 100644
index 0000000..b444c0c
--- /dev/null
+++ b/server/monitor/src/main/java/org/apache/accumulo/monitor/servlets/trace/Summary.java
@@ -0,0 +1,166 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.accumulo.monitor.servlets.trace;
+
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.TreeMap;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.accumulo.core.client.Scanner;
+import org.apache.accumulo.core.data.Key;
+import org.apache.accumulo.core.data.Range;
+import org.apache.accumulo.core.data.Value;
+import org.apache.accumulo.core.trace.TraceFormatter;
+import org.apache.accumulo.monitor.servlets.BasicServlet;
+import org.apache.accumulo.monitor.util.Table;
+import org.apache.accumulo.monitor.util.celltypes.DurationType;
+import org.apache.accumulo.monitor.util.celltypes.NumberType;
+import org.apache.accumulo.monitor.util.celltypes.StringType;
+import org.apache.accumulo.trace.thrift.RemoteSpan;
+import org.apache.hadoop.io.Text;
+
+public class Summary extends Basic {
+
+ private static final long serialVersionUID = 1L;
+ public static final int DEFAULT_MINUTES = 10;
+
+ int getMinutes(HttpServletRequest req) {
+ return getIntParameter(req, "minutes", DEFAULT_MINUTES);
+ }
+
+ @Override
+ public String getTitle(HttpServletRequest req) {
+ return "Traces for the last " + getMinutes(req) + " minutes";
+ }
+
+ static private class Stats {
+ int count;
+ long min = Long.MAX_VALUE;
+ long max = Long.MIN_VALUE;
+ long total = 0l;
+ long histogram[] = new long[] {0, 0, 0, 0, 0, 0};
+
+ void addSpan(RemoteSpan span) {
+ count++;
+ long ms = span.stop - span.start;
+ total += ms;
+ min = Math.min(min, ms);
+ max = Math.max(max, ms);
+ int index = 0;
+ while (ms >= 10 && index < histogram.length) {
+ ms /= 10;
+ index++;
+ }
+ histogram[index]++;
+ }
+
+ long average() {
+ return total / count;
+ }
+ }
+
+ private static class ShowTypeLink extends StringType<String> {
+
+ int minutes;
+
+ public ShowTypeLink(int minutes) {
+ this.minutes = minutes;
+ }
+
+ @Override
+ public String format(Object obj) {
+ if (obj == null)
+ return "-";
+ String type = obj.toString();
+ String encodedType = BasicServlet.encode(type);
+ return String.format("<a href='/trace/listType?type=%s&minutes=%d'>%s</a>", encodedType, minutes, type);
+ }
+ }
+
+ static private class HistogramType extends StringType<Stats> {
+ @Override
+ public String format(Object obj) {
+ Stats stat = (Stats) obj;
+ StringBuilder sb = new StringBuilder();
+ sb.append("<table>");
+ sb.append("<tr>");
+ for (long count : stat.histogram) {
+ if (count > 0)
+ sb.append(String.format("<td style='width:5em'>%d</td>", count));
+ else
+ sb.append("<td style='width:5em'>-</td>");
+ }
+ sb.append("</tr></table>");
+ return sb.toString();
+ }
+
+ @Override
+ public int compare(Stats o1, Stats o2) {
+ for (int i = 0; i < o1.histogram.length; i++) {
+ long diff = o1.histogram[i] - o2.histogram[i];
+ if (diff < 0)
+ return -1;
+ if (diff > 0)
+ return 1;
+ }
+ return 0;
+ }
+ }
+
+ @Override
+ public void pageBody(HttpServletRequest req, HttpServletResponse resp, StringBuilder sb) throws Exception {
+ int minutes = getMinutes(req);
+ long endTime = System.currentTimeMillis();
+ long startTime = endTime - minutes * 60 * 1000;
+
+ Scanner scanner = getScanner(sb);
+ if (scanner == null) {
+ return;
+ }
+ Range range = new Range(new Text("start:" + Long.toHexString(startTime)), new Text("start:" + Long.toHexString(endTime)));
+ scanner.setRange(range);
+ Map<String,Stats> summary = new TreeMap<String,Stats>();
+ for (Entry<Key,Value> entry : scanner) {
+ RemoteSpan span = TraceFormatter.getRemoteSpan(entry);
+ Stats stats = summary.get(span.description);
+ if (stats == null) {
+ summary.put(span.description, stats = new Stats());
+ }
+ stats.addSpan(span);
+ }
+ Table trace = new Table("traceSummary", "All Traces");
+ trace.addSortableColumn("Type", new ShowTypeLink(minutes), "Trace Type");
+ trace.addSortableColumn("Total", new NumberType<Integer>(), "Number of spans of this type");
+ trace.addSortableColumn("min", new DurationType(), "Shortest span duration");
+ trace.addSortableColumn("max", new DurationType(), "Longest span duration");
+ trace.addSortableColumn("avg", new DurationType(), "Average span duration");
+ trace
+ .addSortableColumn(
+ "Histogram",
+ new HistogramType(),
+ "Counts of spans of different duration. Columns start at milliseconds, and each column is ten times longer: tens of milliseconds, seconds, tens of seconds, etc.");
+
+ for (Entry<String,Stats> entry : summary.entrySet()) {
+ Stats stat = entry.getValue();
+ trace.addRow(entry.getKey(), stat.count, stat.min, stat.max, stat.average(), stat);
+ }
+ trace.generate(req, sb);
+ }
+}