/***************************************************************************
* Copyright (c) 2012-2015 VMware, Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
***************************************************************************/
package com.vmware.aurora.stats;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.apache.log4j.Logger;
/**
* This is a lightweight runtime performance profiler.
*
* The profiler keeps track of STAT entries in the system.
* Each STAT entry is defined by user and records accumulative
* counts of invocations of events, requests and code execution.
*
* The profiler periodically dumps the STAT entries and
* reports threads and memory usage.
*
*/
public final class Profiler {
protected static Logger logger = Logger.getLogger(Profiler.class);
/**
* The thread local variable that saves the current STATS source.
* The STATS source typically indicates the triggering event or
* request that causes or invokes the code that follows.
*/
private static ThreadLocal<StatsType> tStatsSrc = new ThreadLocal<StatsType>();
private static final ConcurrentMap<String, StatsEntry> countMap =
new ConcurrentHashMap<>();
/**
* Find or create a matching STATS entry.
*
* Note that the STATS source is retrieved from the thread local variable
* that was previous set by StatsEntry.start() or StatsEntry.push().
*
* @param type current STATS type
* @param objs objects for identifying the STATS entry
* @return the STATS entry
*/
public static StatsEntry getStatsEntry(StatsType type, Object... objs) {
String key = StatsEntry.getKey(getStatsSrc(), type, objs);
StatsEntry val = countMap.get(key);
if (val == null) {
val = new StatsEntry(getStatsSrc(), type, objs);
countMap.put(key, val);
}
return val;
}
/**
* Start a STATS source on the current execution thread.
* @param srcType
*/
public static void start(StatsType srcType) {
tStatsSrc.set(srcType);
}
/**
* Push the Stats type to be used in combination with pop().
*
* void methodA() {
* try {
* StatsType oldType = StatsEntry.push(MY_STAT);
*
* // Any StatsEntry.inc() will have MY_STAT as the "source".
*
* } finally {
* StatsEntry.pop(oldType);
* }
* }
*
* @param srcType the new Stats Type
* @return the old Stats type
*/
public static StatsType push(StatsType srcType) {
StatsType ret = tStatsSrc.get();
tStatsSrc.set(srcType);
return ret;
}
/**
* Reverse StatsEntry.push().
* @param oldSrc
*/
public static void pop(StatsType oldSrc) {
tStatsSrc.set(oldSrc);
}
/**
* This can be used for passing STATS source from a requesting
* thread to a handler thread.
* @return STATS source of the current thread
*/
public static StatsType getStatsSrc() {
return tStatsSrc.get();
}
/**
* Increment the Stats counter and push stats type.
* @return the old Stats type
*/
public static StatsType pushInc(StatsEntry entry) {
entry.inc();
return push(entry.getDest());
}
public static StatsType pushInc(StatsType type, Object... objs) {
return pushInc(getStatsEntry(type, objs));
}
/**
* Increment the Stats counter.
* @param objs
*/
public static void inc(StatsType type, Object... objs) {
getStatsEntry(type, objs).inc();
}
/**
* Log all STATS during the interval.
*/
public static void logInterval(long interval) {
for (StatsEntry e : countMap.values()) {
long count = e.getCount();
long diff = e.updateLastCount(count);
// Only output the items that change.
if (count > 0) {
StatsLogger.logStats("STATS " + interval + " | " + e + ":" +
count + "," + diff);
}
}
}
}