diff --git a/src/main/java/com/ryantenney/metrics/CompositeTimer.java b/src/main/java/com/ryantenney/metrics/CompositeTimer.java
new file mode 100644
index 00000000..2bd478fd
--- /dev/null
+++ b/src/main/java/com/ryantenney/metrics/CompositeTimer.java
@@ -0,0 +1,48 @@
+/**
+ * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us)
+ *
+ * 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.ryantenney.metrics;
+
+import com.codahale.metrics.Timer;
+
+public class CompositeTimer
+{
+ private final Timer totalTimer;
+ private final Timer successTimer;
+ private final Timer failureTimer;
+
+ public CompositeTimer(final Timer totalTimer, final Timer successTimer, final Timer failureTimer)
+ {
+ this.totalTimer = totalTimer;
+ this.successTimer = successTimer;
+ this.failureTimer = failureTimer;
+ }
+
+ public Timer getTotalTimer()
+ {
+ return totalTimer;
+ }
+
+ public Timer getSuccessTimer()
+ {
+ return successTimer;
+ }
+
+ public Timer getFailureTimer()
+ {
+ return failureTimer;
+ }
+}
diff --git a/src/main/java/com/ryantenney/metrics/annotation/CompositeTimed.java b/src/main/java/com/ryantenney/metrics/annotation/CompositeTimed.java
new file mode 100644
index 00000000..044e3f27
--- /dev/null
+++ b/src/main/java/com/ryantenney/metrics/annotation/CompositeTimed.java
@@ -0,0 +1,67 @@
+/**
+ * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us)
+ *
+ * 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.ryantenney.metrics.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * An annotation for marking a method of an annotated object as composite timed.
+ *
+ * Given a method like this:
+ *
+ * {@literal @}CompositeTimed(name = "fancyName")
+ * public String fancyName(String name) {
+ * return "Sir Captain " + name;
+ * }
+ *
+ *
+ * Three timers for the defining class with the name {@code fancyName} will be created:
+ *
+ * - One to time all the method calls
+ * - One to time all successful method calls
+ * - One to time all failing method calls
+ *
+ * and each time the
+ * {@code #fancyName(String)} method is invoked, the method's execution will be timed.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface CompositeTimed {
+ /**
+ * The name of the timer.
+ */
+ String name() default "";
+
+ /**
+ * The suffix for the successful timer.
+ */
+ String successSuffix() default ".success";
+
+ /**
+ * The suffix for the failing timer.
+ */
+ String failedSuffix() default ".failed";
+
+ /**
+ * If {@code true}, use the given name as an absolute name. If {@code false}, use the given name
+ * relative to the annotated class.
+ */
+ boolean absolute() default false;
+}
diff --git a/src/main/java/com/ryantenney/metrics/spring/CompositeTimedMethodInterceptor.java b/src/main/java/com/ryantenney/metrics/spring/CompositeTimedMethodInterceptor.java
new file mode 100644
index 00000000..beed5666
--- /dev/null
+++ b/src/main/java/com/ryantenney/metrics/spring/CompositeTimedMethodInterceptor.java
@@ -0,0 +1,103 @@
+/**
+ * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us)
+ *
+ * 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.ryantenney.metrics.spring;
+
+import com.codahale.metrics.MetricRegistry;
+import com.codahale.metrics.Timer;
+import com.ryantenney.metrics.CompositeTimer;
+import com.ryantenney.metrics.annotation.CompositeTimed;
+import org.aopalliance.aop.Advice;
+import org.aopalliance.intercept.MethodInvocation;
+import org.springframework.aop.Pointcut;
+import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
+import org.springframework.core.Ordered;
+import org.springframework.util.ReflectionUtils;
+
+import java.lang.reflect.Method;
+import java.util.concurrent.TimeUnit;
+
+import static com.ryantenney.metrics.spring.AnnotationFilter.PROXYABLE_METHODS;
+
+class CompositeTimedMethodInterceptor extends AbstractMetricMethodInterceptor implements Ordered
+{
+ public static final Class ANNOTATION = CompositeTimed.class;
+ public static final Pointcut POINTCUT = new AnnotationMatchingPointcut(null, ANNOTATION);
+ public static final ReflectionUtils.MethodFilter METHOD_FILTER = new AnnotationFilter(ANNOTATION, PROXYABLE_METHODS);
+
+ public CompositeTimedMethodInterceptor(final MetricRegistry metricRegistry, final Class> targetClass)
+ {
+ super(metricRegistry, targetClass, ANNOTATION, METHOD_FILTER);
+ }
+
+ @Override
+ protected Object invoke(final MethodInvocation invocation, final CompositeTimer metric, final CompositeTimed annotation) throws Throwable
+ {
+ final Timer.Context timerCtx = metric.getTotalTimer().time();
+ boolean success = true;
+ final Object result;
+ try
+ {
+ result = invocation.proceed();
+ }
+ catch (Throwable t)
+ {
+ success = false;
+ throw t;
+ }
+ finally
+ {
+ final long elapsed = timerCtx.stop();
+ updateTimer(success ? metric.getSuccessTimer() : metric.getFailureTimer(), elapsed);
+ }
+
+ return result;
+ }
+
+ private void updateTimer(final Timer timer, final long elapsed)
+ {
+ timer.update(elapsed, TimeUnit.NANOSECONDS);
+ }
+
+ @Override
+ protected CompositeTimer buildMetric(final MetricRegistry metricRegistry, final String metricName, final CompositeTimed annotation)
+ {
+ final Timer totalTimer = metricRegistry.timer(metricName);
+ final Timer successTimer = metricRegistry.timer(metricName + annotation.successSuffix());
+ final Timer failureTimer = metricRegistry.timer(metricName + annotation.failedSuffix());
+ return new CompositeTimer(totalTimer, successTimer, failureTimer);
+ }
+
+ @Override
+ protected String buildMetricName(final Class> targetClass, final Method method, final CompositeTimed annotation)
+ {
+ return Util.chooseName(annotation.name(), annotation.absolute(), targetClass, method);
+ }
+
+ @Override
+ public int getOrder()
+ {
+ return HIGHEST_PRECEDENCE;
+ }
+
+ static AdviceFactory adviceFactory(final MetricRegistry metricRegistry) {
+ return new AdviceFactory() {
+ @Override
+ public Advice getAdvice(Object bean, Class> targetClass) {
+ return new CompositeTimedMethodInterceptor(metricRegistry, targetClass);
+ }
+ };
+ }
+}
diff --git a/src/main/java/com/ryantenney/metrics/spring/MetricsBeanPostProcessorFactory.java b/src/main/java/com/ryantenney/metrics/spring/MetricsBeanPostProcessorFactory.java
index 724a41ee..7293af8f 100644
--- a/src/main/java/com/ryantenney/metrics/spring/MetricsBeanPostProcessorFactory.java
+++ b/src/main/java/com/ryantenney/metrics/spring/MetricsBeanPostProcessorFactory.java
@@ -37,6 +37,12 @@ public static AdvisingBeanPostProcessor timed(final MetricRegistry metricRegistr
return new AdvisingBeanPostProcessor(TimedMethodInterceptor.POINTCUT, TimedMethodInterceptor.adviceFactory(metricRegistry), proxyConfig);
}
+ public static AdvisingBeanPostProcessor compositeTimed(final MetricRegistry metricRegistry, final ProxyConfig
+ proxyConfig) {
+ return new AdvisingBeanPostProcessor(CompositeTimedMethodInterceptor.POINTCUT, CompositeTimedMethodInterceptor.adviceFactory(metricRegistry),
+ proxyConfig);
+ }
+
public static AdvisingBeanPostProcessor counted(final MetricRegistry metricRegistry, final ProxyConfig proxyConfig) {
return new AdvisingBeanPostProcessor(CountedMethodInterceptor.POINTCUT, CountedMethodInterceptor.adviceFactory(metricRegistry), proxyConfig);
}
diff --git a/src/main/java/com/ryantenney/metrics/spring/Util.java b/src/main/java/com/ryantenney/metrics/spring/Util.java
index 2319d259..fd479170 100755
--- a/src/main/java/com/ryantenney/metrics/spring/Util.java
+++ b/src/main/java/com/ryantenney/metrics/spring/Util.java
@@ -24,6 +24,7 @@
import com.codahale.metrics.annotation.Metered;
import com.codahale.metrics.annotation.Timed;
import com.ryantenney.metrics.annotation.CachedGauge;
+import com.ryantenney.metrics.annotation.CompositeTimed;
import com.ryantenney.metrics.annotation.Counted;
import com.ryantenney.metrics.annotation.Metric;
@@ -35,6 +36,10 @@ static String forTimedMethod(Class> klass, Member member, Timed annotation) {
return chooseName(annotation.name(), annotation.absolute(), klass, member);
}
+ static String forCompositeTimedMethod(Class> klass, Member member, CompositeTimed annotation) {
+ return chooseName(annotation.name(), annotation.absolute(), klass, member);
+ }
+
static String forMeteredMethod(Class> klass, Member member, Metered annotation) {
return chooseName(annotation.name(), annotation.absolute(), klass, member);
}
diff --git a/src/main/java/com/ryantenney/metrics/spring/config/AnnotationDrivenBeanDefinitionParser.java b/src/main/java/com/ryantenney/metrics/spring/config/AnnotationDrivenBeanDefinitionParser.java
index d54ed62d..9eff9cee 100755
--- a/src/main/java/com/ryantenney/metrics/spring/config/AnnotationDrivenBeanDefinitionParser.java
+++ b/src/main/java/com/ryantenney/metrics/spring/config/AnnotationDrivenBeanDefinitionParser.java
@@ -81,6 +81,12 @@ public BeanDefinition parse(Element element, ParserContext parserContext) {
.addConstructorArgReference(metricsBeanName)
.addConstructorArgValue(proxyConfig));
+ registerComponent(parserContext,
+ build(MetricsBeanPostProcessorFactory.class, source, ROLE_INFRASTRUCTURE)
+ .setFactoryMethod("compositeTimed")
+ .addConstructorArgReference(metricsBeanName)
+ .addConstructorArgValue(proxyConfig));
+
registerComponent(parserContext,
build(MetricsBeanPostProcessorFactory.class, source, ROLE_INFRASTRUCTURE)
.setFactoryMethod("counted")
diff --git a/src/main/java/com/ryantenney/metrics/spring/config/annotation/MetricsConfigurationSupport.java b/src/main/java/com/ryantenney/metrics/spring/config/annotation/MetricsConfigurationSupport.java
index 12711bbd..7277675e 100755
--- a/src/main/java/com/ryantenney/metrics/spring/config/annotation/MetricsConfigurationSupport.java
+++ b/src/main/java/com/ryantenney/metrics/spring/config/annotation/MetricsConfigurationSupport.java
@@ -15,6 +15,9 @@
*/
package com.ryantenney.metrics.spring.config.annotation;
+import com.codahale.metrics.MetricRegistry;
+import com.codahale.metrics.health.HealthCheckRegistry;
+import com.ryantenney.metrics.spring.MetricsBeanPostProcessorFactory;
import org.springframework.aop.framework.ProxyConfig;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanPostProcessor;
@@ -25,10 +28,6 @@
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.Assert;
-import com.codahale.metrics.MetricRegistry;
-import com.codahale.metrics.health.HealthCheckRegistry;
-import com.ryantenney.metrics.spring.MetricsBeanPostProcessorFactory;
-
/**
* This is the main class providing the configuration behind the Metrics Java config.
* It is typically imported by adding {@link EnableMetrics @EnableMetrics} to an
@@ -76,6 +75,12 @@ public BeanPostProcessor timedAnnotationBeanPostProcessor() {
return MetricsBeanPostProcessorFactory.timed(getMetricRegistry(), proxyConfig);
}
+ @Bean
+ @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
+ public BeanPostProcessor compositeTimedAnnotationBeanPostProcessor() {
+ return MetricsBeanPostProcessorFactory.compositeTimed(getMetricRegistry(), proxyConfig);
+ }
+
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public BeanPostProcessor countedAnnotationBeanPostProcessor() {
diff --git a/src/test/java/com/ryantenney/metrics/spring/CovariantReturnTypeTest.java b/src/test/java/com/ryantenney/metrics/spring/CovariantReturnTypeTest.java
index e49a9ba1..b2bef513 100644
--- a/src/test/java/com/ryantenney/metrics/spring/CovariantReturnTypeTest.java
+++ b/src/test/java/com/ryantenney/metrics/spring/CovariantReturnTypeTest.java
@@ -15,6 +15,7 @@
*/
package com.ryantenney.metrics.spring;
+import com.ryantenney.metrics.annotation.CompositeTimed;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
@@ -67,6 +68,12 @@ public void testTimedMethod() {
Assert.assertTrue("No metrics should be registered", this.metricRegistry.getNames().isEmpty());
}
+ @Test
+ public void testCompositeTimedMethod() {
+ ctx.getBean(MeteredInterface.class).compositeTimedMethod();
+ Assert.assertTrue("No metrics should be registered", this.metricRegistry.getNames().isEmpty());
+ }
+
@Test
public void testMeteredMethod() {
ctx.getBean(MeteredInterface.class).meteredMethod();
@@ -95,6 +102,9 @@ public interface MeteredInterface {
@Timed
public Number timedMethod();
+ @CompositeTimed
+ public Number compositeTimedMethod();
+
@Metered
public Number meteredMethod();
@@ -113,6 +123,11 @@ public Integer timedMethod() {
return 0;
}
+ @Override
+ public Integer compositeTimedMethod() {
+ return 0;
+ }
+
@Override
public Long meteredMethod() {
return 0L;
diff --git a/src/test/java/com/ryantenney/metrics/spring/EnableMetricsTest.java b/src/test/java/com/ryantenney/metrics/spring/EnableMetricsTest.java
index 588d69e3..df4e4ac4 100755
--- a/src/test/java/com/ryantenney/metrics/spring/EnableMetricsTest.java
+++ b/src/test/java/com/ryantenney/metrics/spring/EnableMetricsTest.java
@@ -15,18 +15,14 @@
*/
package com.ryantenney.metrics.spring;
-import static com.ryantenney.metrics.spring.TestUtil.forCachedGaugeMethod;
-import static com.ryantenney.metrics.spring.TestUtil.forCountedMethod;
-import static com.ryantenney.metrics.spring.TestUtil.forExceptionMeteredMethod;
-import static com.ryantenney.metrics.spring.TestUtil.forGaugeField;
-import static com.ryantenney.metrics.spring.TestUtil.forGaugeMethod;
-import static com.ryantenney.metrics.spring.TestUtil.forMeteredMethod;
-import static com.ryantenney.metrics.spring.TestUtil.forTimedMethod;
+import static com.ryantenney.metrics.spring.TestUtil.*;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat;
+import com.ryantenney.metrics.CompositeTimer;
+import com.ryantenney.metrics.annotation.CompositeTimed;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -136,6 +132,44 @@ public void timedMethod() throws Throwable {
assertThat(timedMethodTimer.getCount(), is(1L));
}
+ @Test
+ public void compositeTimedMethod() throws Throwable {
+ // Verify that the Timer's counter is incremented on method invocation
+ CompositeTimer timedMethodTimer = forCompositeTimedMethod(metricRegistry, TestBean.class, "compositeTimedMethod");
+ assertNotNull(timedMethodTimer);
+ assertNotNull(timedMethodTimer.getTotalTimer());
+ assertNotNull(timedMethodTimer.getSuccessTimer());
+ assertNotNull(timedMethodTimer.getFailureTimer());
+ assertThat(timedMethodTimer.getTotalTimer().getCount(), is(0L));
+ assertThat(timedMethodTimer.getSuccessTimer().getCount(), is(0L));
+ assertThat(timedMethodTimer.getFailureTimer().getCount(), is(0L));
+ testBean.compositeTimedMethod();
+ assertThat(timedMethodTimer.getTotalTimer().getCount(), is(1L));
+ assertThat(timedMethodTimer.getSuccessTimer().getCount(), is(1L));
+ assertThat(timedMethodTimer.getFailureTimer().getCount(), is(0L));
+ }
+
+ @Test
+ public void exceptionCompositeTimedMethod() throws Throwable {
+ // Verify that the Timer's counter is incremented on method invocation
+ CompositeTimer timedMethodTimer = forCompositeTimedMethod(metricRegistry, TestBean.class, "exceptionCompositeTimedMethod");
+ assertNotNull(timedMethodTimer);
+ assertNotNull(timedMethodTimer.getTotalTimer());
+ assertNotNull(timedMethodTimer.getSuccessTimer());
+ assertNotNull(timedMethodTimer.getFailureTimer());
+ assertThat(timedMethodTimer.getTotalTimer().getCount(), is(0L));
+ assertThat(timedMethodTimer.getSuccessTimer().getCount(), is(0L));
+ assertThat(timedMethodTimer.getFailureTimer().getCount(), is(0L));
+ try
+ {
+ testBean.exceptionCompositeTimedMethod();
+ }
+ catch (Throwable t){}
+ assertThat(timedMethodTimer.getTotalTimer().getCount(), is(1L));
+ assertThat(timedMethodTimer.getSuccessTimer().getCount(), is(0L));
+ assertThat(timedMethodTimer.getFailureTimer().getCount(), is(1L));
+ }
+
@Test
public void meteredMethod() throws Throwable {
// Verify that the Meter's counter is incremented on method invocation
@@ -220,6 +254,14 @@ public int cachedGaugeMethod() {
@Timed
public void timedMethod() {}
+ @CompositeTimed
+ public void compositeTimedMethod() {}
+
+ @CompositeTimed
+ public void exceptionCompositeTimedMethod() {
+ throw new RuntimeException();
+ }
+
@Metered
public void meteredMethod() {}
@@ -232,7 +274,6 @@ public void countedMethod(Runnable runnable) {
public void exceptionMeteredMethod() {
throw new RuntimeException();
}
-
}
}
diff --git a/src/test/java/com/ryantenney/metrics/spring/MeteredClassImpementsInterfaceTest.java b/src/test/java/com/ryantenney/metrics/spring/MeteredClassImplementsInterfaceTest.java
similarity index 72%
rename from src/test/java/com/ryantenney/metrics/spring/MeteredClassImpementsInterfaceTest.java
rename to src/test/java/com/ryantenney/metrics/spring/MeteredClassImplementsInterfaceTest.java
index 6a5d227d..0fa021f4 100644
--- a/src/test/java/com/ryantenney/metrics/spring/MeteredClassImpementsInterfaceTest.java
+++ b/src/test/java/com/ryantenney/metrics/spring/MeteredClassImplementsInterfaceTest.java
@@ -15,11 +15,11 @@
*/
package com.ryantenney.metrics.spring;
-import static com.ryantenney.metrics.spring.TestUtil.forCountedMethod;
-import static com.ryantenney.metrics.spring.TestUtil.forMeteredMethod;
-import static com.ryantenney.metrics.spring.TestUtil.forTimedMethod;
+import static com.ryantenney.metrics.spring.TestUtil.*;
import static org.junit.Assert.assertEquals;
+import com.ryantenney.metrics.CompositeTimer;
+import com.ryantenney.metrics.annotation.CompositeTimed;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
@@ -42,7 +42,8 @@
* but annotated at the class level doesn't throw an NPE
* Also verifies that it does register metrics.
*/
-public class MeteredClassImpementsInterfaceTest {
+public class MeteredClassImplementsInterfaceTest
+{
MeteredClassInterface meteredClass;
@@ -84,6 +85,12 @@ public void testTimedMethod() {
Assert.assertFalse("Metrics should be registered", this.metricRegistry.getTimers().isEmpty());
}
+ @Test
+ public void testCompositeTimedMethod() {
+ ctx.getBean(MeteredClassInterface.class).compositeTimedMethod();
+ Assert.assertFalse("Metrics should be registered", this.metricRegistry.getTimers().isEmpty());
+ }
+
@Test
public void testMeteredMethod() {
ctx.getBean(MeteredClassInterface.class).meteredMethod();
@@ -117,6 +124,43 @@ public void timedMethod() throws Throwable {
assertEquals(1, timedMethod.getCount());
}
+ @Test
+ public void compositeTimedMethod() throws Throwable {
+ CompositeTimer timedMethod = forCompositeTimedMethod(metricRegistry, MeteredClassImpl.class,
+ "compositeTimedMethod");
+
+ assertEquals(0, timedMethod.getTotalTimer().getCount());
+ assertEquals(0, timedMethod.getSuccessTimer().getCount());
+ assertEquals(0, timedMethod.getFailureTimer().getCount());
+
+ meteredClass.compositeTimedMethod();
+
+ assertEquals(1, timedMethod.getTotalTimer().getCount());
+ assertEquals(1, timedMethod.getSuccessTimer().getCount());
+ assertEquals(0, timedMethod.getFailureTimer().getCount());
+ }
+
+ @Test
+ public void exceptionCompositeTimedMethod() throws Throwable {
+ CompositeTimer timedMethod = forCompositeTimedMethod(metricRegistry, MeteredClassImpl.class,
+ "exceptionCompositeTimedMethod");
+
+ assertEquals(0, timedMethod.getTotalTimer().getCount());
+ assertEquals(0, timedMethod.getSuccessTimer().getCount());
+ assertEquals(0, timedMethod.getFailureTimer().getCount());
+
+ try{
+ meteredClass.exceptionCompositeTimedMethod();
+ }
+ catch(Throwable e)
+ {
+ }
+
+ assertEquals(1, timedMethod.getTotalTimer().getCount());
+ assertEquals(0, timedMethod.getSuccessTimer().getCount());
+ assertEquals(1, timedMethod.getFailureTimer().getCount());
+ }
+
@Test
public void meteredMethod() throws Throwable {
Meter meteredMethod = forMeteredMethod(metricRegistry, MeteredClassImpl.class, "meteredMethod");
@@ -147,6 +191,10 @@ public interface MeteredClassInterface {
public void timedMethod();
+ public void compositeTimedMethod();
+
+ public void exceptionCompositeTimedMethod() throws Throwable;
+
public void meteredMethod();
public void countedMethod(Runnable runnable);
@@ -161,6 +209,17 @@ public static class MeteredClassImpl implements MeteredClassInterface {
@Timed
public void timedMethod() {}
+ @Override
+ @CompositeTimed
+ public void compositeTimedMethod() {}
+
+ @Override
+ @CompositeTimed
+ public void exceptionCompositeTimedMethod() throws Throwable
+ {
+ throw new BogusException();
+ }
+
@Override
@Metered
public void meteredMethod() {}
diff --git a/src/test/java/com/ryantenney/metrics/spring/MeteredClassTest.java b/src/test/java/com/ryantenney/metrics/spring/MeteredClassTest.java
index 6c6f8b26..acf11b68 100755
--- a/src/test/java/com/ryantenney/metrics/spring/MeteredClassTest.java
+++ b/src/test/java/com/ryantenney/metrics/spring/MeteredClassTest.java
@@ -15,13 +15,7 @@
*/
package com.ryantenney.metrics.spring;
-import static com.ryantenney.metrics.spring.TestUtil.forCachedGaugeMethod;
-import static com.ryantenney.metrics.spring.TestUtil.forCountedMethod;
-import static com.ryantenney.metrics.spring.TestUtil.forExceptionMeteredMethod;
-import static com.ryantenney.metrics.spring.TestUtil.forGaugeField;
-import static com.ryantenney.metrics.spring.TestUtil.forGaugeMethod;
-import static com.ryantenney.metrics.spring.TestUtil.forMeteredMethod;
-import static com.ryantenney.metrics.spring.TestUtil.forTimedMethod;
+import static com.ryantenney.metrics.spring.TestUtil.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@@ -29,6 +23,8 @@
import java.util.concurrent.TimeUnit;
+import com.ryantenney.metrics.CompositeTimer;
+import com.ryantenney.metrics.annotation.CompositeTimed;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
@@ -102,6 +98,31 @@ public void timedMethod() throws Throwable {
assertEquals(2, timedMethod.getCount());
}
+ @Test
+ public void compositeTimedMethod() throws Throwable {
+ CompositeTimer timedMethod = forCompositeTimedMethod(metricRegistry, MeteredClass.class,
+ "compositeTimedMethod");
+
+ assertEquals(0, timedMethod.getTotalTimer().getCount());
+
+ meteredClass.compositeTimedMethod(false);
+ assertEquals(1, timedMethod.getTotalTimer().getCount());
+ assertEquals(1, timedMethod.getSuccessTimer().getCount());
+ assertEquals(0, timedMethod.getFailureTimer().getCount());
+
+ // getCount increments even when the method throws an exception
+ try {
+ meteredClass.compositeTimedMethod(true);
+ fail();
+ }
+ catch (Throwable e) {
+ assertTrue(e instanceof BogusException);
+ }
+ assertEquals(2, timedMethod.getTotalTimer().getCount());
+ assertEquals(1, timedMethod.getSuccessTimer().getCount());
+ assertEquals(1, timedMethod.getFailureTimer().getCount());
+ }
+
@Test
public void meteredMethod() throws Throwable {
Meter meteredMethod = forMeteredMethod(metricRegistry, MeteredClass.class, "meteredMethod");
@@ -186,12 +207,15 @@ public void quadruplyMeteredMethod() throws Throwable {
Timer quadruple_Timed = forTimedMethod(metricRegistry, MeteredClass.class, "quadruplyMeteredMethod");
Meter quadruple_Metered = forMeteredMethod(metricRegistry, MeteredClass.class, "quadruplyMeteredMethod");
Meter quadruple_ExceptionMetered = forExceptionMeteredMethod(metricRegistry, MeteredClass.class, "quadruplyMeteredMethod");
+ CompositeTimer quadruple_CompositeTimed = forCompositeTimedMethod(metricRegistry, MeteredClass.class,
+ "quadruplyMeteredMethod");
final Counter quadruple_Counted = forCountedMethod(metricRegistry, MeteredClass.class, "quadruplyMeteredMethod");
assertEquals(0, quadruple_Metered.getCount());
assertEquals(0, quadruple_Timed.getCount());
assertEquals(0, quadruple_ExceptionMetered.getCount());
assertEquals(0, quadruple_Counted.getCount());
+ assertEquals(0, quadruple_CompositeTimed.getTotalTimer().getCount());
// doesn't throw an exception
meteredClass.quadruplyMeteredMethod(new Runnable() {
@@ -203,6 +227,9 @@ public void run() {
assertEquals(1, quadruple_Metered.getCount());
assertEquals(1, quadruple_Timed.getCount());
+ assertEquals(1, quadruple_CompositeTimed.getTotalTimer().getCount());
+ assertEquals(1, quadruple_CompositeTimed.getSuccessTimer().getCount());
+ assertEquals(0, quadruple_CompositeTimed.getFailureTimer().getCount());
assertEquals(0, quadruple_ExceptionMetered.getCount());
assertEquals(0, quadruple_Counted.getCount());
@@ -222,6 +249,9 @@ public void run() {
}
assertEquals(2, quadruple_Metered.getCount());
assertEquals(2, quadruple_Timed.getCount());
+ assertEquals(2, quadruple_CompositeTimed.getTotalTimer().getCount());
+ assertEquals(1, quadruple_CompositeTimed.getSuccessTimer().getCount());
+ assertEquals(1, quadruple_CompositeTimed.getFailureTimer().getCount());
assertEquals(1, quadruple_ExceptionMetered.getCount());
assertEquals(0, quadruple_Counted.getCount());
}
@@ -263,6 +293,31 @@ public void overloadedTimedMethod() {
assertEquals(2, overloaded_param.getCount());
}
+ @Test
+ public void overloadedCompositeTimedMethod() {
+ Timer compositeOverloaded = metricRegistry.getTimers().get(MetricRegistry.name(MeteredClass.class
+ .getCanonicalName(), "overloaded-compositeTimed"));
+ Timer compositeOverloaded_param = metricRegistry.getTimers().get(MetricRegistry.name(MeteredClass.class.getCanonicalName(), "overloaded-compositeTimed-param"));
+
+ assertEquals(0, compositeOverloaded.getCount());
+ assertEquals(0, compositeOverloaded_param.getCount());
+
+ meteredClass.overloadedCompositeTimedMethod();
+
+ assertEquals(1, compositeOverloaded.getCount());
+ assertEquals(0, compositeOverloaded_param.getCount());
+
+ meteredClass.overloadedCompositeTimedMethod(1);
+
+ assertEquals(1, compositeOverloaded.getCount());
+ assertEquals(1, compositeOverloaded_param.getCount());
+
+ meteredClass.overloadedCompositeTimedMethod(1);
+
+ assertEquals(1, compositeOverloaded.getCount());
+ assertEquals(2, compositeOverloaded_param.getCount());
+ }
+
@Test
public void overloadedMeteredMethod() {
Meter overloaded = metricRegistry.getMeters().get(MetricRegistry.name(MeteredClass.class.getCanonicalName(), "overloaded-metered"));
@@ -438,6 +493,13 @@ public void timedMethod(boolean doThrow) throws Throwable {
}
}
+ @CompositeTimed
+ public void compositeTimedMethod(boolean doThrow) throws Throwable {
+ if (doThrow) {
+ throw new BogusException();
+ }
+ }
+
@Metered
public void meteredMethod() {}
@@ -457,6 +519,7 @@ public void exceptionMeteredMethod(Class clazz) throws
}
@Timed(name = "quadruplyMeteredMethod-timed")
+ @CompositeTimed(name = "quadruplyMeteredMethod-compositeTimed")
@Metered(name = "quadruplyMeteredMethod-metered")
@Counted(name = "quadruplyMeteredMethod-counted")
@ExceptionMetered(name = "quadruplyMeteredMethod-exceptionMetered", cause = BogusException.class)
@@ -470,9 +533,15 @@ public void varargsMeteredMethod(int ... params) {}
@Timed(name = "overloaded-timed")
public void overloadedTimedMethod() {}
+ @CompositeTimed(name = "overloaded-compositeTimed")
+ public void overloadedCompositeTimedMethod() {}
+
@Timed(name = "overloaded-timed-param")
public void overloadedTimedMethod(int param) {}
+ @CompositeTimed(name = "overloaded-compositeTimed-param")
+ public void overloadedCompositeTimedMethod(int param) {}
+
@Metered(name = "overloaded-metered")
public void overloadedMeteredMethod() {}
diff --git a/src/test/java/com/ryantenney/metrics/spring/MeteredInterfaceTest.java b/src/test/java/com/ryantenney/metrics/spring/MeteredInterfaceTest.java
index ac58276e..53de83ed 100755
--- a/src/test/java/com/ryantenney/metrics/spring/MeteredInterfaceTest.java
+++ b/src/test/java/com/ryantenney/metrics/spring/MeteredInterfaceTest.java
@@ -15,6 +15,7 @@
*/
package com.ryantenney.metrics.spring;
+import com.ryantenney.metrics.annotation.CompositeTimed;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
@@ -73,6 +74,12 @@ public void testTimedMethod() {
Assert.assertTrue("No metrics should be registered", this.metricRegistry.getNames().isEmpty());
}
+ @Test
+ public void testCompositeTimedMethod() {
+ ctx.getBean(MeteredInterface.class).compositeTimedMethod();
+ Assert.assertTrue("No metrics should be registered", this.metricRegistry.getNames().isEmpty());
+ }
+
@Test
public void testMeteredMethod() {
ctx.getBean(MeteredInterface.class).meteredMethod();
@@ -101,6 +108,9 @@ public interface MeteredInterface {
@Timed
public void timedMethod();
+ @CompositeTimed
+ public void compositeTimedMethod();
+
@Metered
public void meteredMethod();
@@ -117,6 +127,9 @@ public static class MeteredInterfaceImpl implements MeteredInterface {
@Override
public void timedMethod() {}
+ @Override
+ public void compositeTimedMethod() {}
+
@Override
public void meteredMethod() {}
diff --git a/src/test/java/com/ryantenney/metrics/spring/TestSuite.java b/src/test/java/com/ryantenney/metrics/spring/TestSuite.java
index 3266c6c4..c3ac0f6b 100644
--- a/src/test/java/com/ryantenney/metrics/spring/TestSuite.java
+++ b/src/test/java/com/ryantenney/metrics/spring/TestSuite.java
@@ -29,7 +29,7 @@
CovariantReturnTypeTest.class,
EnableMetricsTest.class,
HealthCheckTest.class,
- MeteredClassImpementsInterfaceTest.class,
+ MeteredClassImplementsInterfaceTest.class,
MeteredClassTest.class,
MeteredInterfaceTest.class,
MetricAnnotationTest.class,
diff --git a/src/test/java/com/ryantenney/metrics/spring/TestUtil.java b/src/test/java/com/ryantenney/metrics/spring/TestUtil.java
index d09c3148..6bcffab5 100755
--- a/src/test/java/com/ryantenney/metrics/spring/TestUtil.java
+++ b/src/test/java/com/ryantenney/metrics/spring/TestUtil.java
@@ -21,6 +21,8 @@
import java.util.ArrayList;
import java.util.List;
+import com.ryantenney.metrics.CompositeTimer;
+import com.ryantenney.metrics.annotation.CompositeTimed;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -41,6 +43,10 @@ class TestUtil {
private static final Logger log = LoggerFactory.getLogger(TestUtil.class);
+ static String forCompositeTimedMethod(Class> klass, Member member, CompositeTimed annotation) {
+ return Util.forCompositeTimedMethod(klass, member, annotation);
+ }
+
static String forTimedMethod(Class> klass, Member member, Timed annotation) {
return Util.forTimedMethod(klass, member, annotation);
}
@@ -97,6 +103,19 @@ static Timer forTimedMethod(MetricRegistry metricRegistry, Class> clazz, Strin
return metricRegistry.getTimers().get(metricName);
}
+ static CompositeTimer forCompositeTimedMethod(MetricRegistry metricRegistry, Class> clazz, String methodName) {
+ Method method = findMethod(clazz, methodName);
+ CompositeTimed annotation = method.getAnnotation(CompositeTimed.class);
+ String metricName = forCompositeTimedMethod(clazz, method, annotation);
+ String successMetricName = metricName + annotation.successSuffix();
+ String failureMetricName = metricName + annotation.failedSuffix();
+ Timer timer = metricRegistry.getTimers().get(metricName);
+ Timer successTimer = metricRegistry.getTimers().get(successMetricName);
+ Timer failedTimer = metricRegistry.getTimers().get(failureMetricName);
+ log.info("Looking up timed method named '{}'", metricName);
+ return new CompositeTimer(timer, successTimer, failedTimer);
+ }
+
static Meter forMeteredMethod(MetricRegistry metricRegistry, Class> clazz, String methodName) {
Method method = findMethod(clazz, methodName);
String metricName = forMeteredMethod(clazz, method, method.getAnnotation(Metered.class));
diff --git a/src/test/resources/metered-interface-impl.xml b/src/test/resources/metered-interface-impl.xml
index 5619e2f1..cafef7ca 100644
--- a/src/test/resources/metered-interface-impl.xml
+++ b/src/test/resources/metered-interface-impl.xml
@@ -28,6 +28,6 @@
-
+
\ No newline at end of file