/* * 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); } }