| /* |
| * Copyright (c) 2004-2009 QOS.ch |
| * All rights reserved. |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining |
| * a copy of this software and associated documentation files (the |
| * "Software"), to deal in the Software without restriction, including |
| * without limitation the rights to use, copy, modify, merge, publish, |
| * distribute, sublicense, and/or sell copies of the Software, and to |
| * permit persons to whom the Software is furnished to do so, subject to |
| * the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be |
| * included in all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
| * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
| * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
| * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| */ |
| |
| package org.slf4j.profiler; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import org.slf4j.Logger; |
| import org.slf4j.Marker; |
| import org.slf4j.MarkerFactory; |
| |
| |
| // + Profiler [BAS] |
| // |-- elapsed time [doX] 0 milliseconds. |
| // |-- elapsed time [doYYYYY] 56 milliseconds. |
| // |--+ Profiler Y |
| // |-- elapsed time [doZ] 21 milliseconds. |
| // |-- elapsed time [doZ] 21 milliseconds. |
| // |-- Total elapsed time [Y] 78 milliseconds. |
| // |-- elapsed time [doZ] 21 milliseconds. |
| // |-- Total elapsed time [BAS] 78 milliseconds. |
| |
| |
| /** |
| * A poor man's profiler to measure the time elapsed performing some lengthy |
| * task. |
| * |
| * @author Ceki Gülcü |
| */ |
| public class Profiler implements TimeInstrument { |
| |
| final static String PROFILER_MARKER_NAME = "PROFILER"; |
| |
| final static int MIN_SW_NAME_LENGTH = 24; |
| final static int MIN_SW_ELAPSED_TIME_NUMBER_LENGTH = 9; |
| |
| final String name; |
| final StopWatch globalStopWatch; |
| |
| List<TimeInstrument> childTimeInstrumentList = new ArrayList<TimeInstrument>(); |
| |
| // optional field |
| ProfilerRegistry profilerRegistry; |
| // optional field |
| Logger logger; |
| |
| public Profiler(String name) { |
| this.name = name; |
| this.globalStopWatch = new StopWatch(name); |
| } |
| |
| public String getName() { |
| return name; |
| } |
| |
| public ProfilerRegistry getProfilerRegistry() { |
| return profilerRegistry; |
| } |
| |
| public void registerWith(ProfilerRegistry profilerRegistry) { |
| if (profilerRegistry == null) { |
| return; |
| } |
| this.profilerRegistry = profilerRegistry; |
| profilerRegistry.put(this); |
| } |
| |
| public Logger getLogger() { |
| return logger; |
| } |
| |
| public void setLogger(Logger logger) { |
| this.logger = logger; |
| } |
| |
| /** |
| * Starts a child stop watch and stops any previously started time |
| * instruments. |
| */ |
| public void start(String name) { |
| stopLastTimeInstrument(); |
| StopWatch childSW = new StopWatch(name); |
| childTimeInstrumentList.add(childSW); |
| } |
| |
| public Profiler startNested(String name) { |
| stopLastTimeInstrument(); |
| Profiler nestedProfiler = new Profiler(name); |
| nestedProfiler.registerWith(profilerRegistry); |
| nestedProfiler.setLogger(logger); |
| childTimeInstrumentList.add(nestedProfiler); |
| return nestedProfiler; |
| } |
| |
| TimeInstrument getLastTimeInstrument() { |
| if (childTimeInstrumentList.size() > 0) { |
| return childTimeInstrumentList.get(childTimeInstrumentList.size() - 1); |
| } else { |
| return null; |
| } |
| } |
| |
| void stopLastTimeInstrument() { |
| TimeInstrument last = getLastTimeInstrument(); |
| if (last != null) { |
| last.stop(); |
| } |
| } |
| |
| // void stopNestedProfilers() { |
| // for (Object child : childTimeInstrumentList) { |
| // if (child instanceof Profiler) |
| // ((Profiler) child).stop(); |
| // } |
| // } |
| |
| public long elapsedTime() { |
| return globalStopWatch.elapsedTime(); |
| } |
| |
| public TimeInstrument stop() { |
| stopLastTimeInstrument(); |
| globalStopWatch.stop(); |
| return this; |
| } |
| |
| public TimeInstrumentStatus getStatus() { |
| return globalStopWatch.status; |
| } |
| |
| /** |
| * This method is used in tests. |
| */ |
| void sanityCheck() throws IllegalStateException { |
| if (getStatus() != TimeInstrumentStatus.STOPPED) { |
| throw new IllegalStateException("time instrument [" + getName() |
| + " is not stopped"); |
| } |
| |
| long totalElapsed = globalStopWatch.elapsedTime(); |
| long childTotal = 0; |
| |
| for (TimeInstrument ti : childTimeInstrumentList) { |
| childTotal += ti.elapsedTime(); |
| if (ti.getStatus() != TimeInstrumentStatus.STOPPED) { |
| throw new IllegalStateException("time instrument [" + ti.getName() |
| + " is not stopped"); |
| } |
| if (ti instanceof Profiler) { |
| Profiler nestedProfiler = (Profiler) ti; |
| nestedProfiler.sanityCheck(); |
| } |
| } |
| if (totalElapsed < childTotal) { |
| throw new IllegalStateException( |
| "children have a higher accumulated elapsed time"); |
| } |
| } |
| |
| static String TOP_PROFILER_FIRST_PREFIX = "+"; |
| static String NESTED_PROFILER_FIRST_PREFIX = "|---+"; |
| static String TOTAL_ELAPSED = " Total "; |
| static String SUBTOTAL_ELAPSED = " Subtotal "; |
| static String ELAPSED_TIME = " elapsed time "; |
| |
| public void print() { |
| System.out.println(toString()); |
| } |
| |
| @Override |
| public String toString() { |
| DurationUnit du = Util.selectDurationUnitForDisplay(globalStopWatch); |
| return buildProfilerString(du, TOP_PROFILER_FIRST_PREFIX, TOTAL_ELAPSED, ""); |
| } |
| |
| public void log() { |
| Marker profilerMarker = MarkerFactory.getMarker(PROFILER_MARKER_NAME); |
| if (logger == null) { |
| throw new NullPointerException( |
| "If you invoke the log() method, then you must associate a logger with this profiler."); |
| } |
| if (logger.isDebugEnabled(profilerMarker)) { |
| DurationUnit du = Util.selectDurationUnitForDisplay(globalStopWatch); |
| String r = buildProfilerString(du, TOP_PROFILER_FIRST_PREFIX, |
| TOTAL_ELAPSED, ""); |
| logger.debug(profilerMarker, SpacePadder.LINE_SEP + r); |
| } |
| } |
| |
| |
| /** |
| * Return a copy of the child instrument list for this Profiler instance. |
| * |
| * @return a copy of this instance's child time instrument list |
| * @since 1.5.9 |
| */ |
| public List<TimeInstrument> getCopyOfChildTimeInstruments() { |
| List<TimeInstrument> copy = new ArrayList<TimeInstrument>(childTimeInstrumentList); |
| return copy; |
| } |
| |
| /** |
| * Return a copy of the global stopwath of this Profiler instance. |
| * |
| * @return a copy of this instance's global stop watch |
| * @since 1.5.9 |
| */ |
| public StopWatch getCopyOfGlobalStopWatch() { |
| StopWatch copy = new StopWatch(globalStopWatch); |
| return copy; |
| } |
| |
| private String buildProfilerString(DurationUnit du, String firstPrefix, |
| String label, String indentation) { |
| StringBuffer buf = new StringBuffer(); |
| |
| buf.append(firstPrefix); |
| buf.append(" Profiler ["); |
| buf.append(name); |
| buf.append("]"); |
| buf.append(SpacePadder.LINE_SEP); |
| for (TimeInstrument child : childTimeInstrumentList) { |
| if (child instanceof StopWatch) { |
| buildStopWatchString(buf, du, ELAPSED_TIME, indentation, |
| (StopWatch) child); |
| } else if (child instanceof Profiler) { |
| Profiler profiler = (Profiler) child; |
| String subString = profiler.buildProfilerString(du, |
| NESTED_PROFILER_FIRST_PREFIX, SUBTOTAL_ELAPSED, indentation |
| + " "); |
| buf.append(subString); |
| buildStopWatchString(buf, du, ELAPSED_TIME, indentation, |
| profiler.globalStopWatch); |
| } |
| } |
| buildStopWatchString(buf, du, label, indentation, globalStopWatch); |
| return buf.toString(); |
| } |
| |
| private static void buildStopWatchString(StringBuffer buf, DurationUnit du, |
| String prefix, String indentation, StopWatch sw) { |
| |
| buf.append(indentation); |
| buf.append("|--"); |
| buf.append(prefix); |
| SpacePadder.leftPad(buf, "[" + sw.getName() + "]", MIN_SW_NAME_LENGTH); |
| buf.append(" "); |
| String timeStr = Util.durationInDurationUnitsAsStr(sw.elapsedTime(), du); |
| SpacePadder.leftPad(buf, timeStr, MIN_SW_ELAPSED_TIME_NUMBER_LENGTH); |
| buf.append(" "); |
| Util.appendDurationUnitAsStr(buf, du); |
| buf.append(SpacePadder.LINE_SEP); |
| } |
| } |