Merge branch '1.5.2-SNAPSHOT' into 1.6.0-SNAPSHOT
Project: http://git-wip-us.apache.org/repos/asf/accumulo/repo Commit: http://git-wip-us.apache.org/repos/asf/accumulo/commit/6831e127 Tree: http://git-wip-us.apache.org/repos/asf/accumulo/tree/6831e127 Diff: http://git-wip-us.apache.org/repos/asf/accumulo/diff/6831e127 Branch: refs/heads/1.6.0-SNAPSHOT Commit: 6831e127afe25bcff015e7dff07edebb5b06d570 Parents: b6dee7f 7ffa80a Author: Josh Elser <els...@apache.org> Authored: Fri Apr 4 21:45:49 2014 -0400 Committer: Josh Elser <els...@apache.org> Committed: Fri Apr 4 21:45:49 2014 -0400 ---------------------------------------------------------------------- .../monitor/servlets/trace/Summary.java | 21 ++++++++-- .../monitor/servlets/trace/SummaryTest.java | 42 ++++++++++++++++++++ 2 files changed, 60 insertions(+), 3 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/accumulo/blob/6831e127/server/monitor/src/main/java/org/apache/accumulo/monitor/servlets/trace/Summary.java ---------------------------------------------------------------------- diff --cc server/monitor/src/main/java/org/apache/accumulo/monitor/servlets/trace/Summary.java index b444c0c,0000000..f717af8 mode 100644,000000..100644 --- 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 @@@ -1,166 -1,0 +1,181 @@@ +/* + * 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; + } + } ++ ++ protected Range getRangeForTrace(long minutesSince) { ++ long endTime = System.currentTimeMillis(); ++ long millisSince = minutesSince * 60 * 1000; ++ // Catch the overflow ++ if (millisSince < minutesSince) { ++ millisSince = endTime; ++ } ++ long startTime = endTime - millisSince; ++ ++ String startHexTime = Long.toHexString(startTime), endHexTime = Long.toHexString(endTime); ++ while (startHexTime.length() < endHexTime.length()) { ++ startHexTime = "0" + startHexTime; ++ } ++ ++ return new Range(new Text("start:" + startHexTime), new Text("start:" + endHexTime)); ++ } + + @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))); ++ Range range = getRangeForTrace(minutes); + 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); + } +}