package com.plexobject.rbac.metric;
import java.lang.management.ManagementFactory;
import java.util.ArrayList;
import java.util.Collection;
import org.apache.log4j.Logger;
import com.plexobject.rbac.Configuration;
/**
* This class collects timing data but it's not thread safe.
*
* @author Shahzad Bhatti
*
*/
public class Timing {
private static final Logger LOGGER = Logger.getLogger(Timing.class);
private static final long ONE_MILL_SEC = 1000000L;
private static final long LOGGING_THRESHOLD_IN_NANOSECS = ONE_MILL_SEC
* Configuration.getInstance().getInteger(
"timer.logging.threshold.millis", 1000);
private final Metric metric;
private static class TimingData {
private long started;
private long ended;
private TimingData() {
this.started = System.nanoTime();
}
private void stop() {
this.ended = System.nanoTime();
}
private long getTotalDurationInNanosecs() {
return ended - started;
}
}
private TimingData currentTimingData;
private Collection<TimingData> elapsedTimingData;
// This class can only be created by Metric and it's using
// package scope for testing.
Timing(final Metric metric) {
this.metric = metric;
this.currentTimingData = new TimingData();
}
/**
* stop - stops the timer
*
* @throws IllegalStateException
* - if timer is used again after stop
*/
public void stop() {
stop("");
}
/**
* stop - stops the timer
*
* @throws IllegalStateException
* - if timer is used again after stop
*/
public void stop(final String logMessage) {
if (currentTimingData == null) {
throw new IllegalStateException("Timer has already been stopped");
}
currentTimingData.stop();
final int totalCalls = elapsedTimingData == null ? 1
: elapsedTimingData.size() + 1;
long totalDurationInNanosecs = currentTimingData
.getTotalDurationInNanosecs();
if (elapsedTimingData != null) {
for (TimingData data : elapsedTimingData) {
totalDurationInNanosecs += data.getTotalDurationInNanosecs();
}
}
metric.finishedTimer(totalCalls, totalDurationInNanosecs);
if (totalDurationInNanosecs > LOGGING_THRESHOLD_IN_NANOSECS
&& LOGGER.isInfoEnabled()) {
LOGGER.info("ended " + logMessage + " for metrics " + metric
+ getSystemStats());
}
currentTimingData = null;
}
/**
* lapse - marks current timing and starts another timer
*
* @throws IllegalStateException
* - if timer is already stopped
*/
public void lapse() {
lapse("");
}
/**
* lapse - marks current timing and starts another timer
*
* @param logMessage
* - will print log message if duration exceeds logging threshold
* @throws IllegalStateException
* - if timer is already stopped
*/
public void lapse(final String logMessage) {
if (currentTimingData == null) {
throw new IllegalStateException("Timer has already been stopped");
}
currentTimingData.stop();
getElapsedTimingData().add(currentTimingData);
if (currentTimingData.getTotalDurationInNanosecs() > LOGGING_THRESHOLD_IN_NANOSECS
&& LOGGER.isInfoEnabled()) {
LOGGER.info("ended " + logMessage + " for metrics " + metric
+ getSystemStats());
}
currentTimingData = null;
}
private Collection<TimingData> getElapsedTimingData() {
if (elapsedTimingData == null) {
elapsedTimingData = new ArrayList<TimingData>();
}
return elapsedTimingData;
}
public static String getSystemStats() {
final StringBuilder sb = new StringBuilder();
Runtime runtime = Runtime.getRuntime();
sb.append(", load: ").append(
String.format("%.2f", ManagementFactory
.getOperatingSystemMXBean().getSystemLoadAverage()));
sb.append(", memory: ").append(runtime.freeMemory() / 1024 / 1024)
.append("M/").append(runtime.totalMemory() / 1024 / 1024 + "M");
sb.append(", threads: ").append(Thread.activeCount());
return sb.toString();
}
}