Implemented a Performance Counter class

This commit is contained in:
Ziver Koc 2017-09-24 00:26:48 +02:00
parent 8ce73e3f50
commit d2ea0e32bd
3 changed files with 320 additions and 1 deletions

View file

@ -169,7 +169,8 @@ public class ClassUtil {
* @return the first class in the stack that do not match the filter
*/
public static String getCallingClass(Class... filter){
ArrayList filterStr = new ArrayList();
ArrayList filterStr = new ArrayList(filter.length + 1);
filterStr.add(ClassUtil.class.getName());
for (Class clazz : filter)
filterStr.add(clazz.getName());

View file

@ -0,0 +1,170 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Ziver Koc
*
* 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 zutil.log;
import zutil.ClassUtil;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import java.lang.management.ManagementFactory;
import java.util.HashMap;
import java.util.concurrent.atomic.AtomicInteger;
/**
* This class is used for performance measurements on an application.
* The counter values will be published in JMX and can be viewed in JConsole.
*/
public class CounterManager {
/** a two dimensional map [Class][Counter ID] for storing singleton objects **/
private static HashMap<String, HashMap<String, Counter>> counters = new HashMap<>();
/**
* @param name a unique name/id of the counter
* @return a singleton instance of the counter name with the calling class as context. Will always return a valid object.
*/
public static Counter getCounter(String name) {
return getCounter(ClassUtil.getCallingClass(CounterManager.class), name);
}
/**
* @param clazz the counter context
* @param name a unique name/id of the counter
* @return a singleton instance of the counter name. Will always return a valid object.
*/
public static Counter getCounter(Class clazz, String name) {
return getCounter(clazz.getName(), name);
}
private static synchronized Counter getCounter(String clazz, String name) {
Counter counter;
if ( ! counters.containsKey(clazz) || ! counters.get(clazz).containsKey(name)) {
// Get the platform MBeanServer
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
// Unique identification of MBeans
counter = new Counter(name);
try {
// Uniquely identify the MBeans and register them with the platform MBeanServer
ObjectName objectName = new ObjectName(clazz + ":name=" + counter.getName());
mbs.registerMBean(counter, objectName);
// Register the singleton
if ( ! counters.containsKey(clazz))
counters.put(clazz, new HashMap<String, Counter>());
counters.get(clazz).put(name, counter);
} catch (Exception e) {
e.printStackTrace();
}
}
else
counter = counters.get(clazz).get(name);
return counter;
}
/**
* MBean Counter definition for publishing data in JMX
*/
public interface CounterMBean {
String getName();
int getValue();
int getMax();
int getMin();
double getAverage();
}
public static class Counter implements CounterMBean{
private final String name;
private int max;
private int min;
private double average;
private int sampleCount;
private AtomicInteger counter = new AtomicInteger();
protected Counter(String name){
this.name = name;
}
public void add(int i){
int prev = counter.getAndAdd(i);
updateMetaData(prev + i);
}
public void set(int i){
counter.getAndSet(i);
updateMetaData(i);
}
public void increment(){
int i = counter.incrementAndGet();
updateMetaData(i);
}
public void decrement(){
int i = counter.decrementAndGet();
updateMetaData(i);
}
private void updateMetaData(int i){
if (max < i)
max = i;
if (min > i)
min = i;
average = (average*sampleCount + i) / ++sampleCount;
}
@Override
public String getName() {
return name;
}
/**
* @return current value of the counter
*/
@Override
public int getValue(){
return counter.intValue();
}
/**
* @return the maximum registered value over the lifetime of the counter
*/
@Override
public int getMax() {
return max;
}
/**
* @return the minimum registered value over the lifetime of the counter
*/
@Override
public int getMin() {
return min;
}
/**
* @return the average value of the counter
*/
@Override
public double getAverage() {
return average;
}
}
}

View file

@ -0,0 +1,148 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Ziver Koc
*
* 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 zutil.log;
import org.junit.Test;
import zutil.log.CounterManager.Counter;
import static org.junit.Assert.*;
public class CounterManagerTest {
@Test
public void singletonTest(){
Counter c1 = CounterManager.getCounter("TestCounter");
Counter c2 = CounterManager.getCounter("TestCounter");
assertSame(c1, c2);
c1 = CounterManager.getCounter("TestCounter");
c2 = CounterManager.getCounter(CounterManagerTest.class, "TestCounter");
assertSame(c1, c2);
c1 = CounterManager.getCounter(CounterManagerTest.class, "TestCounter");
c2 = CounterManager.getCounter(CounterManagerTest.class, "TestCounter");
assertSame(c1, c2);
}
@Test
public void addTest(){
Counter c1 = CounterManager.getCounter("AddTestCounter");
assertEquals(0, c1.getValue());
c1.add(10);
assertEquals(10, c1.getValue());
c1.add(20);
c1.add(30);
assertEquals(60, c1.getValue());
}
@Test
public void setTest(){
Counter c1 = CounterManager.getCounter("SetTestCounter");
assertEquals(0, c1.getValue());
c1.set(10);
assertEquals(10, c1.getValue());
c1.set(20);
c1.set(30);
assertEquals(30, c1.getValue());
}
@Test
public void incrementTest(){
Counter c1 = CounterManager.getCounter("IncrementTestCounter");
assertEquals(0, c1.getValue());
c1.increment();
assertEquals(1, c1.getValue());
c1.increment();
c1.increment();
c1.increment();
assertEquals(4, c1.getValue());
}
@Test
public void decrementTest(){
Counter c1 = CounterManager.getCounter("DecrementTestCounter");
assertEquals(0, c1.getValue());
c1.decrement();
assertEquals(-1, c1.getValue());
c1.decrement();
c1.decrement();
c1.decrement();
assertEquals(-4, c1.getValue());
}
@Test
public void maxTest(){
Counter c1 = CounterManager.getCounter("MaxTestCounter");
assertEquals(0, c1.getMax());
c1.set(10);
assertEquals(10, c1.getMax());
c1.set(50);
assertEquals(50, c1.getMax());
c1.set(0);
assertEquals(50, c1.getMax());
}
@Test
public void minTest(){
Counter c1 = CounterManager.getCounter("MinTestCounter");
assertEquals(0, c1.getMin());
c1.set(10);
assertEquals(0, c1.getMin());
c1.set(-50);
assertEquals(-50, c1.getMin());
c1.set(0);
assertEquals(-50, c1.getMin());
}
@Test
public void averageTest(){
Counter c1 = CounterManager.getCounter("MinTestCounter");
assertEquals(0, (int)c1.getAverage());
c1.set(10);
assertEquals(10, (int)c1.getAverage());
c1.set(10);
assertEquals(10, (int)c1.getAverage());
c1.set(70);
assertEquals(30, (int)c1.getAverage());
}
}