I try to get statitics on method execution using a annotation on my code.
Here is the code for the annotation
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.util.function.Supplier;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Timer;
public class StatsMethodCall {
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Stats {
}
public static class StatsRegistry {
private MetricRegistry metricRegistry;
public StatsRegistry(final MetricRegistry metricRegistry) {
this.metricRegistry = metricRegistry;
}
public void addInvocation(final String methodName, final long duration) {
Timer timer = metricRegistry.timer(methodName);
timer.update(duration, java.util.concurrent.TimeUnit.MILLISECONDS);
}
public Timer getTimer(final String methodName) {
return metricRegistry.timer(methodName);
}
}
/**
* Get the statistics based on the method name in the Stack.
* This is a work around because it doesn't require the @Stats Annotation.
* Check Method statsMethod2.
* @param <T>
* @param method
* @param statsRegistry
* @return
*/
public static <T> T statsMethod(final Supplier<T> method, final StatsRegistry statsRegistry) {
final String methodName = Thread.currentThread().getStackTrace()[2].getMethodName();
T result;
final Timer.Context timerContext = statsRegistry.getTimer(methodName).time();
try {
result = method.get();
} finally {
timerContext.stop();
}
return result;
}
/**
* Get the statistics based on the method with @Stats Annotation.
* Doesn't work has expected
* @param <T>
* @param method
* @param statsRegistry
* @return
*/
public static <T> T statsMethod2(final Supplier<T> method, final StatsRegistry statsRegistry) {
String methodName = null;
for (final StackTraceElement element : Thread.currentThread().getStackTrace()) {
try {
final Method m = Class.forName(element.getClassName()).getDeclaredMethod(element.getMethodName());
if (m.getAnnotation(Stats.class) != null) {
methodName = m.getName();
break;
}
} catch (final ClassNotFoundException | NoSuchMethodException e) {
// do nothing
}
}
if (methodName == null) {
throw new IllegalStateException("Cannot find method annotated with @" + Stats.class.getSimpleName());
}
T result;
final Timer.Context timerContext = statsRegistry.getTimer(methodName).time();
try {
result = method.get();
} finally {
timerContext.stop();
}
return result;
}
}
And I try to test the code with JUnit
import java.util.function.Supplier;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Timer;
class StatsMethodCallTest {
static MetricRegistry metricRegistry = new MetricRegistry();
static StatsRegistry registry = new StatsRegistry(metricRegistry);
static MyClass myClass = new MyClass();
public static class MyClass {
@Stats
public String myMethod(final String arg1, final int arg2, final double arg3) {
try {
Thread.sleep(10);
} catch (final InterruptedException ie) {
ie.printStackTrace();
}
final String result = "Hello " + arg1 + " " + arg2 + " " + arg3;
System.out.println(result);
return result;
}
}
@BeforeAll
static void setUpBeforeClass() throws Exception {
metricRegistry = new MetricRegistry();
registry = new StatsRegistry(metricRegistry);
}
@AfterAll
static void tearDownAfterClass() throws Exception {
}
@BeforeEach
void setUp() throws Exception {
}
@AfterEach
void tearDown() throws Exception {
}
/**
* Workaround - I'm not fan of this
*/
@Test
final void test() {
for(int i =0; i <100; i++) {
//Here is the correct call
StatsMethodCall.statsMethod(() -> myClass.myMethod("foo", 42, 3.14), registry);
}
//I tried without Lambda Expression just in case it was related to this
for(int i = 0; i < 100; i++) {
StatsMethodCall.statsMethod(new Supplier<Object>() {
@Override
public Object get() {
return myClass.myMethod("foo", 42, 3.14);
}
}, registry);
}
//This works but is weird code
Timer timer = registry.getTimer("test");
System.out.println("Invocation count: " + timer.getCount());
System.out.println("Min duration: " + timer.getSnapshot().getMin());
System.out.println("Max duration: " + timer.getSnapshot().getMax());
System.out.println("Avg duration: " + timer.getSnapshot().getMean());
//This doesn't work
Timer timer2 = registry.getTimer(MyClass.class.getDeclaredMethods()[0].getName());
System.out.println("Invocation count: " + timer2.getCount());
System.out.println("Min duration: " + timer2.getSnapshot().getMin());
System.out.println("Max duration: " + timer2.getSnapshot().getMax());
System.out.println("Avg duration: " + timer2.getSnapshot().getMean());
}
/**
* This is how I would like it works
* Note: I'm calling statsMethod2 here !
*/
@Test
final void test2() {
for(int i =0; i <100; i++) {
//Here is the correct call
StatsMethodCall.statsMethod2(() -> myClass.myMethod("foo", 42, 3.14), registry);
}
Timer timer = registry.getTimer(MyClass.class.getDeclaredMethods()[0].getName());
System.out.println("Invocation count: " + timer.getCount());
System.out.println("Min duration: " + timer.getSnapshot().getMin());
System.out.println("Max duration: " + timer.getSnapshot().getMax());
System.out.println("Avg duration: " + timer.getSnapshot().getMean());
}
}
Help and explanation highly appreciated.
Thanks Kind regards
I expect that the Java Reflexion ill find the method with an anntation @Stats.
But there is no such method with @Stats annotation in the stack Very strange
Aucun commentaire:
Enregistrer un commentaire