/* * Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com * The software in this package is published under the terms of the CPAL v1.0 * license, a copy of which has been included with this distribution in the * LICENSE.txt file. */ package org.mule.runtime.core.management.stats; import org.mule.runtime.core.api.management.stats.Statistics; import org.mule.runtime.core.management.stats.printers.SimplePrinter; import org.mule.runtime.core.util.StringUtils; import java.io.PrintWriter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * ComponentStatistics is a basic metrics aggregation class that is accessible via the JMX api. This class is not thread-safe - * occasional errors in reported statistics should be expected, especially when the {@link #clear()} method is used. */ public class ComponentStatistics implements Statistics { private static final Logger logger = LoggerFactory.getLogger(ComponentStatistics.class); /** * Serial version */ private static final long serialVersionUID = -2086999226732861674L; private long minExecutionTime = 0; private long maxExecutionTime = 0; private long averageExecutionTime = 0; private long executedEvent = 0; private long totalExecTime = 0; private boolean enabled = false; private long intervalTime = 0; private long currentIntervalStartTime = 0; private boolean statIntervalTimeEnabled = false; /** * The constructor added to initialize the interval time in ms that stats are measured for from the property statIntervalTime. * If the property is not set or cannot be parsed, disable interval time and just compute stats from start of mule. * <p/> * TODO: The code to create and use an interval time for measuring average execution time could be removed once a complete * solution is available in MuleHQ to monitor this */ public ComponentStatistics() { String intervalTimeString = System.getProperty("statIntervalTime"); if (StringUtils.isBlank(intervalTimeString)) { statIntervalTimeEnabled = false; } else { try { intervalTime = Integer.parseInt(intervalTimeString); statIntervalTimeEnabled = true; } catch (NumberFormatException e) { // just disable it statIntervalTimeEnabled = false; logger.warn("Couldn't parse statIntervalTime: " + intervalTimeString + ". Disabled."); } } } /** * Resets the state of this component statistics collector. * <p/> * If called while a branch is being executed, then statistics may be slightly erroneous. */ public void clear() { minExecutionTime = 0; maxExecutionTime = 0; executedEvent = 0; totalExecTime = 0; averageExecutionTime = 0; } /** * Returns true if this stats collector is enabled. * <p/> * This value does not affect statistics tabulation directly - it is up to the component to enable/disable collection based on * the value of this method. * * @return {@code true} if stats collection is enabled, otherwise false. */ @Override public boolean isEnabled() { return enabled; } public void logSummary() { logSummary(new SimplePrinter(System.out)); } public void logSummary(PrintWriter printer) { printer.print(this); } /** * Tags this stats collector as enabled or disabled. * <p/> * Does not affect stats calculation - it is up to the caller to check this flag. * * @param enabled {@code true} if stats should be enabled, otherwise false. */ public void setEnabled(boolean enabled) { this.enabled = enabled; } /** * The maximum total event execution time seen since last cleared. * * @return The maximum time, or zero if no events have been started. */ public long getMaxExecutionTime() { return maxExecutionTime; } /** * The minimum total event execution time seen since last cleared. * * @return The maximum time, or zero if no events have been completed. */ public long getMinExecutionTime() { return minExecutionTime; } /** * The total cumulative execution time since statistics were last cleared. Includes the sum of all branch times plus any * directly recorded execution times. * * @return The total cumulative execution time, in milliseconds. */ public long getTotalExecutionTime() { return totalExecTime; } /** * Then number of events executed since last cleared. * <p/> * NOTE: When branch times are recorded, an event will typically be recorded as 'executed' on the first branch event. See * {@link #addExecutionBranchTime(boolean, long, long)}. * * @return The number of events executed since last cleared. */ public long getExecutedEvents() { return executedEvent; } /** * Add a new execution-time measurement for one branch of processing an event. * * @param first true if this is the first branch for this event * @param branch the time to execute this branch * @param total the total time (so far) for processing this event */ public synchronized void addExecutionBranchTime(boolean first, long branch, long total) { // TODO MULE-9151 - ComponentStatistics should really create distinct Event // objects that can be used to aggregate statistics and then atomically // log them at completion time. if (statIntervalTimeEnabled) { long currentTime = System.currentTimeMillis(); if (currentIntervalStartTime == 0) { currentIntervalStartTime = currentTime; } if ((currentTime - currentIntervalStartTime) > intervalTime) { clear(); currentIntervalStartTime = currentTime; } } if (first) { executedEvent++; } if (executedEvent > 0) { totalExecTime += ProcessingTime.getEffectiveTime(branch); long effectiveTotal = ProcessingTime.getEffectiveTime(total); if (maxExecutionTime == 0 || effectiveTotal > maxExecutionTime) { maxExecutionTime = effectiveTotal; } averageExecutionTime = totalExecTime / executedEvent; } } /** * Add the complete execution time for a flow that also reports branch execution times. * <p/> * Use in conjunction with {@link #addExecutionBranchTime(boolean, long, long)}. * * @param time the total time required to process this event */ public synchronized void addCompleteExecutionTime(long time) { if (executedEvent > 0) { long effectiveTime = ProcessingTime.getEffectiveTime(time); if (minExecutionTime == 0 || effectiveTime < minExecutionTime) { minExecutionTime = effectiveTime; } } } /** * Add a new execution-time measurement for processing an event. * <p/> * Do not use when reporting branch execution times; instead see {@link #addCompleteExecutionTime(long)}. * * @param time The total event time to be logged/recorded. */ public synchronized void addExecutionTime(long time) { if (statIntervalTimeEnabled) { long currentTime = System.currentTimeMillis(); if (currentIntervalStartTime == 0) { currentIntervalStartTime = currentTime; } if ((currentTime - currentIntervalStartTime) > intervalTime) { clear(); currentIntervalStartTime = currentTime; } } executedEvent++; long effectiveTime = ProcessingTime.getEffectiveTime(time); totalExecTime += effectiveTime; if (minExecutionTime == 0 || effectiveTime < minExecutionTime) { minExecutionTime = time; } if (maxExecutionTime == 0 || effectiveTime > maxExecutionTime) { maxExecutionTime = time; } averageExecutionTime = totalExecTime / executedEvent; } /** * Returns the average execution time, rounded downwards. * * @return the total event time accumulated to this point, divided by the total number of events recorded. */ public long getAverageExecutionTime() { return averageExecutionTime; } }