001package org.javasimon.javaee.reqreporter; 002 003import java.util.HashMap; 004import java.util.List; 005import java.util.Map; 006import java.util.Set; 007import java.util.TreeSet; 008import javax.servlet.http.HttpServletRequest; 009 010import org.javasimon.Split; 011import org.javasimon.javaee.SimonServletFilter; 012import org.javasimon.utils.SimonUtils; 013 014/** 015 * Reports significant splits (longer than 5% of the request) and list of all used stopwatches with their split counts. 016 * Report is sent through {@link org.javasimon.Manager#message(String)}. Following aspects of the class can be overridden: 017 * <ul> 018 * <li>Where the report goes - override {@link #reportMessage(String)},</li> 019 * <li>what is significant split - override {@link #isSignificantSplit(org.javasimon.Split, org.javasimon.Split)},</li> 020 * <li>whether stopwatch info (from stopwatch distribution part) should be included - 021 * override {@link #shouldBeAddedStopwatchInfo(ReporterStopwatchInfo)}.</li> 022 * </ul> 023 * 024 * @author <a href="mailto:virgo47@gmail.com">Richard "Virgo" Richter</a> 025 */ 026public class DefaultRequestReporter implements RequestReporter { 027 private static final int NOTE_OUTPUT_MAX_LEN = 80; 028 029 private SimonServletFilter simonServletFilter; 030 031 public DefaultRequestReporter() { 032 } 033 034 @Override 035 public void reportRequest(HttpServletRequest request, Split requestSplit, List<Split> splits) { 036 StringBuilder messageBuilder = new StringBuilder( 037 "Web request is too long (" + SimonUtils.presentNanoTime(requestSplit.runningFor()) + 038 ") [" + requestSplit.getStopwatch().getNote() + "]"); 039 040 if (splits.size() > 0) { 041 buildSplitDetails(requestSplit, splits, messageBuilder); 042 } 043 044 reportMessage(messageBuilder.toString()); 045 } 046 047 /** 048 * Reports the prepared message through the method {@link org.javasimon.Manager#message(String)} - can be overridden 049 * to emit the message to log/console/etc. 050 * 051 * @param message prepared message with report 052 */ 053 protected void reportMessage(String message) { 054 simonServletFilter.getManager().message(message); 055 } 056 057 private void buildSplitDetails(Split requestSplit, List<Split> splits, StringBuilder messageBuilder) { 058 Map<String, ReporterStopwatchInfo> stopwatchInfos = new HashMap<>(); 059 060 processSplitsAndAddSignificantOnes(requestSplit, splits, messageBuilder, stopwatchInfos); 061 addStopwatchSplitDistribution(messageBuilder, stopwatchInfos); 062 } 063 064 private void processSplitsAndAddSignificantOnes(Split requestSplit, List<Split> splits, StringBuilder messageBuilder, Map<String, ReporterStopwatchInfo> stopwatchInfos) { 065 for (Split split : splits) { 066 ReporterStopwatchInfo stopwatchInfo = stopwatchInfos.get(split.getStopwatch().getName()); 067 if (stopwatchInfo == null) { 068 stopwatchInfo = new ReporterStopwatchInfo(split.getStopwatch()); 069 stopwatchInfos.put(split.getStopwatch().getName(), stopwatchInfo); 070 } 071 stopwatchInfo.addSplit(split); 072 073 if (isSignificantSplit(split, requestSplit)) { 074 messageBuilder.append("\n\t").append(split.getStopwatch().getName()).append(": "). 075 append(SimonUtils.presentNanoTime(split.runningFor())); 076 } 077 } 078 } 079 080 /** 081 * Can be overridden to decide whether {@link Split} is considered significant to be reported in the first part of the output. 082 * By default all Splits with time over 5% of total request time are significant. This includes overlapping splits too, so more than 083 * 20 splits can be reported. 084 * 085 * @param split tested Split 086 * @param requestSplit Split for the whole HTTP request 087 * @return true, if tested Split is significant 088 */ 089 protected boolean isSignificantSplit(Split split, Split requestSplit) { 090 return split.runningFor() > (requestSplit.runningFor() / 20); // is more than 5% 091 } 092 093 private void addStopwatchSplitDistribution(StringBuilder messageBuilder, Map<String, ReporterStopwatchInfo> stopwatchInfos) { 094 messageBuilder.append("\nStopwatch/Split count/total/max for this request (sorted by total descending):"); 095 Set<ReporterStopwatchInfo> sortedInfos = new TreeSet<>(stopwatchInfos.values()); 096 for (ReporterStopwatchInfo info : sortedInfos) { 097 if (shouldBeAddedStopwatchInfo(info)) { 098 addStopwatchInfo(messageBuilder, info); 099 } 100 } 101 } 102 103 /** 104 * Decides whether stopwatch info should be included in the report - by default all are included. 105 * 106 * @param info stopwatch info contains list of all splits, max split and total time of splits for the reported request 107 * @return true, if the stopatch info should be reported 108 */ 109 @SuppressWarnings("UnusedParameters") 110 protected boolean shouldBeAddedStopwatchInfo(ReporterStopwatchInfo info) { 111 return true; 112 } 113 114 private void addStopwatchInfo(StringBuilder messageBuilder, ReporterStopwatchInfo info) { 115 messageBuilder.append("\n\t").append(info.stopwatch.getName()).append(": ").append(info.splits.size()). 116 append("x, total: ").append(SimonUtils.presentNanoTime(info.total)). 117 append(", max: ").append(SimonUtils.presentNanoTime(info.maxSplit.runningFor())); 118 if (info.stopwatch.getNote() != null) { 119 messageBuilder.append(", note: ").append(SimonUtils.compact(info.stopwatch.getNote(), NOTE_OUTPUT_MAX_LEN)); 120 } 121 } 122 123 @Override 124 public void setSimonServletFilter(SimonServletFilter simonServletFilter) { 125 this.simonServletFilter = simonServletFilter; 126 } 127}