├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── SHADE-README.md ├── checkstyle.xml ├── eclipse-formatter.xml ├── findbugs-exclude.xml ├── infinitest.filters ├── pom.xml └── src ├── main ├── java │ └── com │ │ └── ryantenney │ │ └── metrics │ │ ├── annotation │ │ ├── CachedGauge.java │ │ ├── Counted.java │ │ └── Metric.java │ │ └── spring │ │ ├── AbstractAnnotationBeanPostProcessor.java │ │ ├── AbstractMetricMethodInterceptor.java │ │ ├── AdviceFactory.java │ │ ├── AdvisingBeanPostProcessor.java │ │ ├── AnnotationFilter.java │ │ ├── CachedGaugeAnnotationBeanPostProcessor.java │ │ ├── CountedMethodInterceptor.java │ │ ├── ExceptionMeteredMethodInterceptor.java │ │ ├── GaugeFieldAnnotationBeanPostProcessor.java │ │ ├── GaugeMethodAnnotationBeanPostProcessor.java │ │ ├── HealthCheckBeanPostProcessor.java │ │ ├── LegacyCachedGaugeAnnotationBeanPostProcessor.java │ │ ├── LegacyCountedMethodInterceptor.java │ │ ├── LegacyMetricAnnotationBeanPostProcessor.java │ │ ├── MeteredMethodInterceptor.java │ │ ├── MethodKey.java │ │ ├── MetricAnnotationBeanPostProcessor.java │ │ ├── MetricsBeanPostProcessorFactory.java │ │ ├── TimedMethodInterceptor.java │ │ ├── Util.java │ │ ├── config │ │ ├── AnnotationDrivenBeanDefinitionParser.java │ │ ├── HealthCheckRegistryBeanDefinitionParser.java │ │ ├── MetricRegistryBeanDefinitionParser.java │ │ ├── MetricsNamespaceHandler.java │ │ ├── RegisterMetricBeanDefinitionParser.java │ │ ├── ReporterBeanDefinitionParser.java │ │ └── annotation │ │ │ ├── DelegatingMetricsConfiguration.java │ │ │ ├── EnableMetrics.java │ │ │ ├── MetricsConfigurationSupport.java │ │ │ ├── MetricsConfigurer.java │ │ │ ├── MetricsConfigurerAdapter.java │ │ │ └── MetricsConfigurerComposite.java │ │ ├── reporter │ │ ├── AbstractReporterElementParser.java │ │ ├── AbstractReporterFactoryBean.java │ │ ├── AbstractScheduledReporterFactoryBean.java │ │ ├── BasicMetricPrefixSupplier.java │ │ ├── ConsoleReporterElementParser.java │ │ ├── ConsoleReporterFactoryBean.java │ │ ├── CsvReporterElementParser.java │ │ ├── CsvReporterFactoryBean.java │ │ ├── DatadogReporterElementParser.java │ │ ├── DatadogReporterFactoryBean.java │ │ ├── ElasticSearchReporterElementParser.java │ │ ├── ElasticSearchReporterFactoryBean.java │ │ ├── GangliaReporterElementParser.java │ │ ├── GangliaReporterFactoryBean.java │ │ ├── GraphiteReporterElementParser.java │ │ ├── GraphiteReporterFactoryBean.java │ │ ├── JmxReporterElementParser.java │ │ ├── JmxReporterFactoryBean.java │ │ ├── LibratoReporterElementParser.java │ │ ├── LibratoReporterFactoryBean.java │ │ ├── MetricPrefixSupplier.java │ │ ├── NewRelicReporterElementParser.java │ │ ├── NewRelicReporterFactoryBean.java │ │ ├── ReporterElementParser.java │ │ ├── Slf4jReporterElementParser.java │ │ └── Slf4jReporterFactoryBean.java │ │ └── servlets │ │ └── MetricsServletsContextListener.java └── resources │ ├── META-INF │ ├── services │ │ └── com.ryantenney.metrics.spring.reporter.ReporterElementParser │ ├── spring.handlers │ ├── spring.schemas │ └── spring.tooling │ └── com │ └── ryantenney │ └── metrics │ └── spring │ └── config │ ├── metrics-3.0.xsd │ ├── metrics-3.1.xsd │ ├── metrics.gif │ └── metrics.xsd └── test ├── java └── com │ └── ryantenney │ └── metrics │ └── spring │ ├── AopFieldInjectionInteractionTest.java │ ├── CovariantReturnTypeTest.java │ ├── EnableMetricsTest.java │ ├── HealthCheckTest.java │ ├── LegacyAnnotationMeteredClassTest.java │ ├── LegacyMetricAnnotationTest.java │ ├── LoggingMetricRegistryListener.java │ ├── MeteredClassImpementsInterfaceTest.java │ ├── MeteredClassTest.java │ ├── MeteredInterfaceTest.java │ ├── MetricAnnotationTest.java │ ├── ProxyTargetClassTest.java │ ├── RegisterElementTest.java │ ├── RegistryTest.java │ ├── ReporterTest.java │ ├── SharedRegistryTest.java │ ├── TestSuite.java │ ├── TestUtil.java │ ├── benchmarks │ └── MeterBenchmark.java │ └── reporter │ ├── FakeReporter.java │ ├── FakeReporterElementParser.java │ └── FakeReporterFactoryBean.java └── resources ├── META-INF └── services │ └── com.ryantenney.metrics.spring.reporter.ReporterElementParser ├── aop-field-injection-interaction.xml ├── covariant-return-type.xml ├── custom-registries.xml ├── default-registries.xml ├── fake-reporter-test.xml ├── health-check.xml ├── injected-metrics.xml ├── legacy-annotation-metered-class.xml ├── legacy-metric-annotation.xml ├── logback-test.xml ├── metered-class.xml ├── metered-interface-impl.xml ├── metered-interface.xml ├── metric-annotation.xml ├── proxy-target-class-disabled.xml ├── proxy-target-class-enabled.xml ├── register-element-test.xml ├── reporter-placeholder-test.xml ├── reporter-test.xml ├── shared-registry.xml └── supplied-registries.xml /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | .classpath 3 | .project 4 | .settings/ 5 | .idea/ 6 | /*.iml 7 | /*.ipr 8 | /*.iws 9 | .DS_Store 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | 3 | #avoid bug in : https://github.com/travis-ci/travis-ci/issues/5227 4 | before_install: 5 | - cat /etc/hosts # optionally check the content *before* 6 | - sudo hostname "$(hostname | cut -c1-63)" 7 | - sed -e "s/^\\(127\\.0\\.0\\.1.*\\)/\\1 $(hostname | cut -c1-63)/" /etc/hosts | sudo tee /etc/hosts 8 | - cat /etc/hosts # optionally check the content *after* 9 | 10 | # don't just run the tests, also run Findbugs and friends 11 | script: mvn verify 12 | 13 | jdk: 14 | - oraclejdk7 15 | - openjdk7 16 | 17 | notifications: 18 | # Email notifications are disabled to not annoy anybody. 19 | # See http://about.travis-ci.org/docs/user/build-configuration/ to learn more 20 | # about configuring notification recipients and more. 21 | email: false 22 | -------------------------------------------------------------------------------- /SHADE-README.md: -------------------------------------------------------------------------------- 1 | To use metrics-spring in a project which uses the maven-shade-plugin, it is necessary to configure several resource transformers to instruct shade on how to merge resources. 2 | 3 | _Without this Spring will not work correctly and reporters will not be detected._ 4 | 5 | Sample shade plugin configuration: 6 | ```xml 7 | 8 | org.apache.maven.plugins 9 | maven-shade-plugin 10 | 2.1 11 | 12 | 13 | package 14 | 15 | shade 16 | 17 | 18 | 19 | 20 | META-INF/spring.handlers 21 | 22 | 23 | META-INF/spring.schemas 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | ``` 32 | -------------------------------------------------------------------------------- /findbugs-exclude.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /infinitest.filters: -------------------------------------------------------------------------------- 1 | .* -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/annotation/CachedGauge.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.annotation; 17 | 18 | import java.lang.annotation.ElementType; 19 | import java.lang.annotation.Retention; 20 | import java.lang.annotation.RetentionPolicy; 21 | import java.lang.annotation.Target; 22 | import java.util.concurrent.TimeUnit; 23 | 24 | /** 25 | * An annotation for marking a method as a gauge, which caches the result for a specified time. 26 | * 27 | *

28 | * Given a method like this: 29 | *

30 |  *     {@literal @}CachedGauge(name = "queueSize", timeout = 30, timeoutUnit = TimeUnit.SECONDS)
31 |  *     public int getQueueSize() {
32 |  *         return queue.getSize();
33 |  *     }
34 |  *
35 |  * 
36 | *

37 | * 38 | * A gauge for the defining class with the name queueSize will be created which uses the annotated method's 39 | * return value as its value, and which caches the result for 30 seconds. 40 | */ 41 | @Deprecated 42 | @com.codahale.metrics.annotation.CachedGauge(timeout = 0) 43 | @Retention(RetentionPolicy.RUNTIME) 44 | @Target(ElementType.METHOD) 45 | public @interface CachedGauge { 46 | 47 | /** 48 | * The name of the counter. 49 | */ 50 | String name() default ""; 51 | 52 | /** 53 | * If {@code true}, use the given name as an absolute name. If {@code false}, use the given name 54 | * relative to the annotated class. 55 | */ 56 | boolean absolute() default false; 57 | 58 | /** 59 | * The amount of time to cache the result 60 | */ 61 | long timeout(); 62 | 63 | /** 64 | * The unit of timeout 65 | */ 66 | TimeUnit timeoutUnit() default TimeUnit.MILLISECONDS; 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/annotation/Counted.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.annotation; 17 | 18 | import java.lang.annotation.ElementType; 19 | import java.lang.annotation.Retention; 20 | import java.lang.annotation.RetentionPolicy; 21 | import java.lang.annotation.Target; 22 | 23 | /** 24 | * An annotation for marking a method of an annotated object as counted. 25 | * 26 | *

27 | * Given a method like this: 28 | *

29 |  *     {@literal @}Counted(name = "fancyName")
30 |  *     public String fancyName(String name) {
31 |  *         return "Sir Captain " + name;
32 |  *     }
33 |  * 
34 | *

35 | * A counter for the defining class with the name {@code fancyName} will be created and each time the 36 | * {@code #fancyName(String)} method is invoked, the counter will be marked. 37 | */ 38 | @Deprecated 39 | @com.codahale.metrics.annotation.Counted 40 | @Retention(RetentionPolicy.RUNTIME) 41 | @Target(ElementType.METHOD) 42 | public @interface Counted { 43 | 44 | /** 45 | * The name of the counter. 46 | */ 47 | String name() default ""; 48 | 49 | /** 50 | * If {@code true}, use the given name as an absolute name. If {@code false}, use the given name 51 | * relative to the annotated class. 52 | */ 53 | boolean absolute() default false; 54 | 55 | /** 56 | * If {@code false} (default), counter is decremented when the annotated 57 | * method returns, counts current invocations of the annotated method. 58 | * If {@code true}, counter increases monotonically, counts total number 59 | * of invocations of the annotated method. 60 | */ 61 | boolean monotonic() default false; 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/annotation/Metric.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.annotation; 17 | 18 | import java.lang.annotation.ElementType; 19 | import java.lang.annotation.Retention; 20 | import java.lang.annotation.RetentionPolicy; 21 | import java.lang.annotation.Target; 22 | 23 | /** 24 | * An annotation requesting that a metric be injected or registered. 25 | * 26 | *

27 | * Given a field like this: 28 | *

29 |  *     {@literal @}Metric
30 |  *     public Histogram histogram;
31 |  * 
32 | *

33 | * A meter of the field's type will be created and injected into Spring-managed beans after construction 34 | * but before initialization. It will be up to the user to interact with the metric. This annotation 35 | * can be used on fields of type Meter, Timer, Counter, and Histogram. 36 | * 37 | *

38 | * This may also be used to register a metric, which is useful for creating a histogram with 39 | * a custom Reservoir. 40 | *

41 | *

42 |  *     {@literal @}Metric
43 |  *     public Histogram uniformHistogram = new Histogram(new UniformReservoir());
44 |  * 
45 | * 46 | */ 47 | @Deprecated 48 | @com.codahale.metrics.annotation.Metric 49 | @Retention(RetentionPolicy.RUNTIME) 50 | @Target(ElementType.FIELD) 51 | public @interface Metric { 52 | 53 | /** 54 | * The name of the metric. 55 | */ 56 | String name() default ""; 57 | 58 | /** 59 | * If {@code true}, use the given name as an absolute name. If {@code false}, use the given name 60 | * relative to the annotated class. 61 | */ 62 | boolean absolute() default false; 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/spring/AbstractAnnotationBeanPostProcessor.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring; 17 | 18 | import java.lang.reflect.Field; 19 | import java.lang.reflect.Method; 20 | 21 | import org.slf4j.Logger; 22 | import org.slf4j.LoggerFactory; 23 | import org.springframework.beans.factory.config.BeanPostProcessor; 24 | import org.springframework.util.ReflectionUtils.FieldCallback; 25 | import org.springframework.util.ReflectionUtils.MethodCallback; 26 | 27 | import static org.springframework.aop.support.AopUtils.getTargetClass; 28 | import static org.springframework.util.ReflectionUtils.doWithFields; 29 | import static org.springframework.util.ReflectionUtils.doWithMethods; 30 | 31 | abstract class AbstractAnnotationBeanPostProcessor implements BeanPostProcessor { 32 | 33 | protected final Logger LOG = LoggerFactory.getLogger(getClass()); 34 | 35 | public static enum Members { 36 | FIELDS, METHODS, ALL 37 | } 38 | 39 | public static enum Phase { 40 | PRE_INIT, POST_INIT; 41 | } 42 | 43 | private final Members members; 44 | private final Phase phase; 45 | private final AnnotationFilter filter; 46 | 47 | public AbstractAnnotationBeanPostProcessor(final Members members, final Phase phase, final AnnotationFilter filter) { 48 | this.members = members; 49 | this.phase = phase; 50 | this.filter = filter; 51 | } 52 | 53 | /** 54 | * @param bean 55 | * @param beanName 56 | * @param targetClass 57 | * @param field 58 | */ 59 | protected void withField(Object bean, String beanName, Class targetClass, Field field) {} 60 | 61 | /** 62 | * @param bean 63 | * @param beanName 64 | * @param targetClass 65 | * @param method 66 | */ 67 | protected void withMethod(Object bean, String beanName, Class targetClass, Method method) {} 68 | 69 | @Override 70 | public final Object postProcessBeforeInitialization(Object bean, String beanName) { 71 | if (phase == Phase.PRE_INIT) { 72 | process(bean, beanName); 73 | } 74 | 75 | return bean; 76 | } 77 | 78 | @Override 79 | public final Object postProcessAfterInitialization(Object bean, String beanName) { 80 | if (phase == Phase.POST_INIT) { 81 | process(bean, beanName); 82 | } 83 | 84 | return bean; 85 | } 86 | 87 | private void process(final Object bean, final String beanName) { 88 | final Class targetClass = getTargetClass(bean); 89 | 90 | if (members == Members.FIELDS || members == Members.ALL) { 91 | doWithFields(targetClass, new FieldCallback() { 92 | @Override 93 | public void doWith(Field field) throws IllegalAccessException { 94 | withField(bean, beanName, targetClass, field); 95 | } 96 | }, filter); 97 | } 98 | 99 | if (members == Members.METHODS || members == Members.ALL) { 100 | doWithMethods(targetClass, new MethodCallback() { 101 | @Override 102 | public void doWith(final Method method) throws IllegalAccessException { 103 | withMethod(bean, beanName, targetClass, method); 104 | } 105 | }, filter); 106 | } 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/spring/AbstractMetricMethodInterceptor.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring; 17 | 18 | import java.lang.annotation.Annotation; 19 | import java.lang.reflect.Method; 20 | import java.util.HashMap; 21 | import java.util.Map; 22 | 23 | import org.aopalliance.intercept.MethodInterceptor; 24 | import org.aopalliance.intercept.MethodInvocation; 25 | import org.slf4j.Logger; 26 | import org.slf4j.LoggerFactory; 27 | import org.springframework.util.ReflectionUtils; 28 | import org.springframework.util.ReflectionUtils.MethodCallback; 29 | import org.springframework.util.ReflectionUtils.MethodFilter; 30 | 31 | import com.codahale.metrics.MetricRegistry; 32 | 33 | abstract class AbstractMetricMethodInterceptor implements MethodInterceptor, MethodCallback { 34 | 35 | protected final Logger LOG = LoggerFactory.getLogger(getClass()); 36 | 37 | private final MetricRegistry metricRegistry; 38 | private final Class targetClass; 39 | private final Class annotationClass; 40 | private final Map> metrics; 41 | 42 | AbstractMetricMethodInterceptor(final MetricRegistry metricRegistry, final Class targetClass, final Class annotationClass, 43 | final MethodFilter methodFilter) { 44 | this.metricRegistry = metricRegistry; 45 | this.targetClass = targetClass; 46 | this.annotationClass = annotationClass; 47 | this.metrics = new HashMap>(); 48 | 49 | LOG.debug("Creating method interceptor for class {}", targetClass.getCanonicalName()); 50 | LOG.debug("Scanning for @{} annotated methods", annotationClass.getSimpleName()); 51 | 52 | ReflectionUtils.doWithMethods(targetClass, this, methodFilter); 53 | } 54 | 55 | @Override 56 | public Object invoke(MethodInvocation invocation) throws Throwable { 57 | final AnnotationMetricPair annotationMetricPair = metrics.get(MethodKey.forMethod(invocation.getMethod())); 58 | if (annotationMetricPair != null) { 59 | return invoke(invocation, annotationMetricPair.getMeter(), annotationMetricPair.getAnnotation()); 60 | } 61 | else { 62 | return invocation.proceed(); 63 | } 64 | } 65 | 66 | @Override 67 | public void doWith(Method method) throws IllegalAccessException { 68 | final A annotation = method.getAnnotation(annotationClass); 69 | if (annotation != null) { 70 | final MethodKey methodKey = MethodKey.forMethod(method); 71 | final String metricName = buildMetricName(targetClass, method, annotation); 72 | final M metric = buildMetric(metricRegistry, metricName, annotation); 73 | 74 | if (metric != null) { 75 | metrics.put(methodKey, new AnnotationMetricPair(annotation, metric)); 76 | 77 | if (LOG.isDebugEnabled()) { 78 | LOG.debug("Created {} {} for method {}", metric.getClass().getSimpleName(), metricName, methodKey); 79 | } 80 | } 81 | } 82 | } 83 | 84 | protected abstract String buildMetricName(Class targetClass, Method method, A annotation); 85 | 86 | protected abstract M buildMetric(MetricRegistry metricRegistry, String metricName, A annotation); 87 | 88 | protected abstract Object invoke(MethodInvocation invocation, M metric, A annotation) throws Throwable; 89 | 90 | public static final class AnnotationMetricPair { 91 | 92 | private final A annotation; 93 | private final M meter; 94 | 95 | public AnnotationMetricPair(final A annotation, final M meter) { 96 | this.annotation = annotation; 97 | this.meter = meter; 98 | } 99 | 100 | public A getAnnotation() { 101 | return annotation; 102 | } 103 | 104 | public M getMeter() { 105 | return meter; 106 | } 107 | 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/spring/AdviceFactory.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring; 17 | 18 | import org.aopalliance.aop.Advice; 19 | 20 | interface AdviceFactory { 21 | 22 | Advice getAdvice(Object bean, Class targetClass); 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/spring/AdvisingBeanPostProcessor.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring; 17 | 18 | import java.util.concurrent.ConcurrentHashMap; 19 | import java.util.concurrent.ConcurrentMap; 20 | 21 | import org.aopalliance.aop.Advice; 22 | import org.slf4j.Logger; 23 | import org.slf4j.LoggerFactory; 24 | import org.springframework.aop.Advisor; 25 | import org.springframework.aop.Pointcut; 26 | import org.springframework.aop.framework.Advised; 27 | import org.springframework.aop.framework.AopInfrastructureBean; 28 | import org.springframework.aop.framework.ProxyConfig; 29 | import org.springframework.aop.framework.ProxyFactory; 30 | import org.springframework.aop.support.AopUtils; 31 | import org.springframework.aop.support.DefaultPointcutAdvisor; 32 | import org.springframework.beans.factory.config.BeanPostProcessor; 33 | import org.springframework.util.ClassUtils; 34 | 35 | class AdvisingBeanPostProcessor implements BeanPostProcessor { 36 | 37 | private static final Logger LOG = LoggerFactory.getLogger(AdvisingBeanPostProcessor.class); 38 | 39 | private final ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader(); 40 | 41 | private final Pointcut pointcut; 42 | private final AdviceFactory adviceFactory; 43 | private final ProxyConfig proxyConfig; 44 | 45 | private final ConcurrentMap, Boolean> applicabilityCache = new ConcurrentHashMap, Boolean>(256); 46 | 47 | public AdvisingBeanPostProcessor(final Pointcut pointcut, final AdviceFactory adviceFactory, final ProxyConfig proxyConfig) { 48 | this.pointcut = pointcut; 49 | this.adviceFactory = adviceFactory; 50 | this.proxyConfig = proxyConfig; 51 | } 52 | 53 | @Override 54 | public Object postProcessBeforeInitialization(Object bean, String beanName) { 55 | return bean; 56 | } 57 | 58 | @Override 59 | public Object postProcessAfterInitialization(Object bean, String beanName) { 60 | if (bean instanceof AopInfrastructureBean) { 61 | return bean; 62 | } 63 | 64 | final Class targetClass = AopUtils.getTargetClass(bean); 65 | 66 | if (canApply(targetClass)) { 67 | final Advice advice = adviceFactory.getAdvice(bean, targetClass); 68 | final Advisor advisor = new DefaultPointcutAdvisor(pointcut, advice); 69 | 70 | if (bean instanceof Advised) { 71 | LOG.debug("Bean {} is already proxied, adding Advisor to existing proxy", beanName); 72 | 73 | ((Advised) bean).addAdvisor(0, advisor); 74 | 75 | return bean; 76 | } 77 | else { 78 | LOG.debug("Proxying bean {} of type {}", beanName, targetClass.getCanonicalName()); 79 | 80 | final ProxyFactory proxyFactory = new ProxyFactory(bean); 81 | if (proxyConfig != null) { 82 | proxyFactory.copyFrom(proxyConfig); 83 | } 84 | proxyFactory.addAdvisor(advisor); 85 | 86 | final Object proxy = proxyFactory.getProxy(this.beanClassLoader); 87 | 88 | return proxy; 89 | } 90 | } 91 | 92 | return bean; 93 | } 94 | 95 | private boolean canApply(final Class targetClass) { 96 | Boolean cachedApplicability = applicabilityCache.get(targetClass); 97 | if (cachedApplicability != null) { 98 | return (boolean) cachedApplicability; 99 | } 100 | 101 | boolean canApply = AopUtils.canApply(pointcut, targetClass); 102 | applicabilityCache.putIfAbsent(targetClass, canApply); 103 | return canApply; 104 | } 105 | 106 | } 107 | -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/spring/AnnotationFilter.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring; 17 | 18 | import java.lang.annotation.Annotation; 19 | import java.lang.reflect.Field; 20 | import java.lang.reflect.Member; 21 | import java.lang.reflect.Method; 22 | import java.lang.reflect.Modifier; 23 | 24 | import org.slf4j.Logger; 25 | import org.slf4j.LoggerFactory; 26 | import org.springframework.util.ReflectionUtils.FieldFilter; 27 | import org.springframework.util.ReflectionUtils.MethodFilter; 28 | 29 | import static org.springframework.util.ReflectionUtils.USER_DECLARED_METHODS; 30 | import static java.lang.reflect.Modifier.*; 31 | 32 | class AnnotationFilter implements MethodFilter, FieldFilter { 33 | 34 | private static final Logger LOG = LoggerFactory.getLogger(AnnotationFilter.class); 35 | 36 | public static final int FIELDS = 37 | Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE | 38 | Modifier.STATIC | Modifier.FINAL | Modifier.TRANSIENT | 39 | Modifier.VOLATILE; 40 | 41 | public static final int METHODS = 42 | Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE | 43 | Modifier.ABSTRACT | Modifier.STATIC | Modifier.FINAL | 44 | Modifier.SYNCHRONIZED | Modifier.NATIVE | Modifier.STRICT | 45 | Modifier.TRANSIENT; // TRANSIENT flag is the same as the VARARGS flag 46 | 47 | public static final int INJECTABLE_FIELDS = FIELDS ^ (FINAL | STATIC); 48 | public static final int INSTANCE_FIELDS = FIELDS ^ STATIC; 49 | public static final int INSTANCE_METHODS = METHODS ^ (ABSTRACT | STATIC); 50 | public static final int PROXYABLE_METHODS = METHODS ^ (ABSTRACT | FINAL | PRIVATE | STATIC); 51 | 52 | private final Class clazz; 53 | private final int methodModifiers; 54 | private final int fieldModifiers; 55 | 56 | public AnnotationFilter(final Class clazz) { 57 | this(clazz, METHODS, FIELDS); 58 | } 59 | 60 | public AnnotationFilter(final Class clazz, final int modifiers) { 61 | this(clazz, modifiers, modifiers); 62 | } 63 | 64 | public AnnotationFilter(final Class clazz, final int methodModifiers, final int fieldModifiers) { 65 | this.clazz = clazz; 66 | this.methodModifiers = methodModifiers & METHODS; 67 | this.fieldModifiers = fieldModifiers & FIELDS; 68 | } 69 | 70 | @Override 71 | public boolean matches(Method method) { 72 | if (USER_DECLARED_METHODS.matches(method) && method.isAnnotationPresent(clazz)) { 73 | if (checkModifiers(method, methodModifiers)) { 74 | return true; 75 | } 76 | else { 77 | LOG.warn("Ignoring @{} on method {}.{} due to illegal modifiers: {}", clazz.getSimpleName(), method.getDeclaringClass().getCanonicalName(), 78 | method.getName(), Modifier.toString(method.getModifiers() & ~methodModifiers)); 79 | } 80 | } 81 | return false; 82 | } 83 | 84 | @Override 85 | public boolean matches(Field field) { 86 | if (field.isAnnotationPresent(clazz)) { 87 | if (checkModifiers(field, fieldModifiers)) { 88 | return true; 89 | } 90 | else { 91 | LOG.warn("Ignoring @{} on field {}.{} due to illegal modifiers: {}", clazz.getSimpleName(), field.getDeclaringClass().getCanonicalName(), 92 | field.getName(), Modifier.toString(field.getModifiers() & ~fieldModifiers)); 93 | } 94 | } 95 | return false; 96 | } 97 | 98 | private boolean checkModifiers(Member member, int allowed) { 99 | int modifiers = member.getModifiers(); 100 | return (modifiers & allowed) == modifiers; 101 | } 102 | 103 | @Override 104 | public String toString() { 105 | return "[AnnotationFilter: @" + clazz.getSimpleName() + ", methodModifiers: (" + Modifier.toString(methodModifiers) + "), fieldModifiers: (" 106 | + Modifier.toString(fieldModifiers) + ")]"; 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/spring/CachedGaugeAnnotationBeanPostProcessor.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring; 17 | 18 | import java.lang.reflect.Method; 19 | 20 | import org.springframework.core.Ordered; 21 | import org.springframework.util.ReflectionUtils; 22 | 23 | import com.codahale.metrics.MetricRegistry; 24 | import com.codahale.metrics.annotation.CachedGauge; 25 | 26 | import static com.ryantenney.metrics.spring.AnnotationFilter.INSTANCE_METHODS; 27 | 28 | class CachedGaugeAnnotationBeanPostProcessor extends AbstractAnnotationBeanPostProcessor implements Ordered { 29 | 30 | private static final AnnotationFilter FILTER = new AnnotationFilter(CachedGauge.class, INSTANCE_METHODS); 31 | 32 | private final MetricRegistry metrics; 33 | 34 | public CachedGaugeAnnotationBeanPostProcessor(final MetricRegistry metrics) { 35 | super(Members.METHODS, Phase.POST_INIT, FILTER); 36 | this.metrics = metrics; 37 | } 38 | 39 | @Override 40 | protected void withMethod(final Object bean, String beanName, Class targetClass, final Method method) { 41 | if (method.getParameterTypes().length > 0) { 42 | throw new IllegalStateException("Method " + method.getName() + " is annotated with @CachedGauge but requires parameters."); 43 | } 44 | 45 | final CachedGauge annotation = method.getAnnotation(CachedGauge.class); 46 | final String metricName = Util.forCachedGauge(targetClass, method, annotation); 47 | 48 | metrics.register(metricName, new com.codahale.metrics.CachedGauge(annotation.timeout(), annotation.timeoutUnit()) { 49 | @Override 50 | protected Object loadValue() { 51 | return ReflectionUtils.invokeMethod(method, bean); 52 | } 53 | }); 54 | 55 | LOG.debug("Created cached gauge {} for method {}.{}", metricName, targetClass.getCanonicalName(), method.getName()); 56 | } 57 | 58 | @Override 59 | public int getOrder() { 60 | return LOWEST_PRECEDENCE; 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/spring/CountedMethodInterceptor.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring; 17 | 18 | import java.lang.reflect.Method; 19 | 20 | import org.aopalliance.aop.Advice; 21 | import org.aopalliance.intercept.MethodInvocation; 22 | import org.springframework.aop.Pointcut; 23 | import org.springframework.aop.support.annotation.AnnotationMatchingPointcut; 24 | import org.springframework.util.ReflectionUtils.MethodFilter; 25 | 26 | import com.codahale.metrics.Counter; 27 | import com.codahale.metrics.MetricRegistry; 28 | import com.codahale.metrics.annotation.Counted; 29 | 30 | import static com.ryantenney.metrics.spring.AnnotationFilter.PROXYABLE_METHODS; 31 | 32 | class CountedMethodInterceptor extends AbstractMetricMethodInterceptor { 33 | 34 | public static final Class ANNOTATION = Counted.class; 35 | public static final Pointcut POINTCUT = new AnnotationMatchingPointcut(null, ANNOTATION); 36 | public static final MethodFilter METHOD_FILTER = new AnnotationFilter(ANNOTATION, PROXYABLE_METHODS); 37 | 38 | public CountedMethodInterceptor(final MetricRegistry metricRegistry, final Class targetClass) { 39 | super(metricRegistry, targetClass, ANNOTATION, METHOD_FILTER); 40 | } 41 | 42 | @Override 43 | protected Object invoke(MethodInvocation invocation, Counter counter, Counted annotation) throws Throwable { 44 | try { 45 | counter.inc(); 46 | return invocation.proceed(); 47 | } 48 | finally { 49 | if (!annotation.monotonic()) { 50 | counter.dec(); 51 | } 52 | } 53 | } 54 | 55 | @Override 56 | protected Counter buildMetric(MetricRegistry metricRegistry, String metricName, Counted annotation) { 57 | return metricRegistry.counter(metricName); 58 | } 59 | 60 | @Override 61 | protected String buildMetricName(Class targetClass, Method method, Counted annotation) { 62 | return Util.forCountedMethod(targetClass, method, annotation); 63 | } 64 | 65 | static AdviceFactory adviceFactory(final MetricRegistry metricRegistry) { 66 | return new AdviceFactory() { 67 | @Override 68 | public Advice getAdvice(Object bean, Class targetClass) { 69 | return new CountedMethodInterceptor(metricRegistry, targetClass); 70 | } 71 | }; 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/spring/ExceptionMeteredMethodInterceptor.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring; 17 | 18 | import java.lang.reflect.Method; 19 | 20 | import org.aopalliance.aop.Advice; 21 | import org.aopalliance.intercept.MethodInvocation; 22 | import org.springframework.aop.Pointcut; 23 | import org.springframework.aop.support.annotation.AnnotationMatchingPointcut; 24 | import org.springframework.core.Ordered; 25 | import org.springframework.util.ReflectionUtils.MethodFilter; 26 | 27 | import com.codahale.metrics.Meter; 28 | import com.codahale.metrics.MetricRegistry; 29 | import com.codahale.metrics.annotation.ExceptionMetered; 30 | 31 | import static com.ryantenney.metrics.spring.AnnotationFilter.PROXYABLE_METHODS; 32 | 33 | class ExceptionMeteredMethodInterceptor extends AbstractMetricMethodInterceptor implements Ordered { 34 | 35 | public static final Class ANNOTATION = ExceptionMetered.class; 36 | public static final Pointcut POINTCUT = new AnnotationMatchingPointcut(null, ANNOTATION); 37 | public static final MethodFilter METHOD_FILTER = new AnnotationFilter(ANNOTATION, PROXYABLE_METHODS); 38 | 39 | public ExceptionMeteredMethodInterceptor(final MetricRegistry metricRegistry, final Class targetClass) { 40 | super(metricRegistry, targetClass, ANNOTATION, METHOD_FILTER); 41 | } 42 | 43 | @Override 44 | protected Object invoke(MethodInvocation invocation, Meter meter, ExceptionMetered annotation) throws Throwable { 45 | try { 46 | return invocation.proceed(); 47 | } 48 | catch (Throwable t) { 49 | if (annotation.cause().isAssignableFrom(t.getClass())) { 50 | meter.mark(); 51 | } 52 | throw t; 53 | } 54 | } 55 | 56 | @Override 57 | protected Meter buildMetric(MetricRegistry metricRegistry, String metricName, ExceptionMetered annotation) { 58 | return metricRegistry.meter(metricName); 59 | } 60 | 61 | @Override 62 | protected String buildMetricName(Class targetClass, Method method, ExceptionMetered annotation) { 63 | return Util.forExceptionMeteredMethod(targetClass, method, annotation); 64 | } 65 | 66 | @Override 67 | public int getOrder() { 68 | return HIGHEST_PRECEDENCE; 69 | } 70 | 71 | static AdviceFactory adviceFactory(final MetricRegistry metricRegistry) { 72 | return new AdviceFactory() { 73 | @Override 74 | public Advice getAdvice(Object bean, Class targetClass) { 75 | return new ExceptionMeteredMethodInterceptor(metricRegistry, targetClass); 76 | } 77 | }; 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/spring/GaugeFieldAnnotationBeanPostProcessor.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring; 17 | 18 | import java.lang.reflect.Field; 19 | 20 | import org.springframework.core.Ordered; 21 | import org.springframework.util.ReflectionUtils; 22 | 23 | import com.codahale.metrics.MetricRegistry; 24 | import com.codahale.metrics.annotation.Gauge; 25 | 26 | import static com.ryantenney.metrics.spring.AnnotationFilter.INSTANCE_FIELDS; 27 | 28 | class GaugeFieldAnnotationBeanPostProcessor extends AbstractAnnotationBeanPostProcessor implements Ordered { 29 | 30 | private static final AnnotationFilter FILTER = new AnnotationFilter(Gauge.class, INSTANCE_FIELDS); 31 | 32 | private final MetricRegistry metrics; 33 | 34 | public GaugeFieldAnnotationBeanPostProcessor(final MetricRegistry metrics) { 35 | super(Members.ALL, Phase.PRE_INIT, FILTER); 36 | this.metrics = metrics; 37 | } 38 | 39 | @Override 40 | protected void withField(final Object bean, String beanName, Class targetClass, final Field field) { 41 | ReflectionUtils.makeAccessible(field); 42 | 43 | final Gauge annotation = field.getAnnotation(Gauge.class); 44 | final String metricName = Util.forGauge(targetClass, field, annotation); 45 | 46 | metrics.register(metricName, new com.codahale.metrics.Gauge() { 47 | @Override 48 | public Object getValue() { 49 | Object value = ReflectionUtils.getField(field, bean); 50 | if (value instanceof com.codahale.metrics.Gauge) { 51 | value = ((com.codahale.metrics.Gauge) value).getValue(); 52 | } 53 | return value; 54 | } 55 | }); 56 | 57 | LOG.debug("Created gauge {} for field {}.{}", metricName, targetClass.getCanonicalName(), field.getName()); 58 | } 59 | 60 | @Override 61 | public int getOrder() { 62 | return LOWEST_PRECEDENCE; 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/spring/GaugeMethodAnnotationBeanPostProcessor.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring; 17 | 18 | import java.lang.reflect.Method; 19 | 20 | import org.springframework.core.Ordered; 21 | import org.springframework.util.ReflectionUtils; 22 | 23 | import com.codahale.metrics.MetricRegistry; 24 | import com.codahale.metrics.annotation.Gauge; 25 | 26 | import static com.ryantenney.metrics.spring.AnnotationFilter.INSTANCE_METHODS; 27 | 28 | class GaugeMethodAnnotationBeanPostProcessor extends AbstractAnnotationBeanPostProcessor implements Ordered { 29 | 30 | private static final AnnotationFilter FILTER = new AnnotationFilter(Gauge.class, INSTANCE_METHODS); 31 | 32 | private final MetricRegistry metrics; 33 | 34 | public GaugeMethodAnnotationBeanPostProcessor(final MetricRegistry metrics) { 35 | super(Members.ALL, Phase.POST_INIT, FILTER); 36 | this.metrics = metrics; 37 | } 38 | 39 | @Override 40 | protected void withMethod(final Object bean, String beanName, Class targetClass, final Method method) { 41 | if (method.getParameterTypes().length > 0) { 42 | throw new IllegalStateException("Method " + method.getName() + " is annotated with @Gauge but requires parameters."); 43 | } 44 | 45 | final Gauge annotation = method.getAnnotation(Gauge.class); 46 | final String metricName = Util.forGauge(targetClass, method, annotation); 47 | 48 | metrics.register(metricName, new com.codahale.metrics.Gauge() { 49 | @Override 50 | public Object getValue() { 51 | return ReflectionUtils.invokeMethod(method, bean); 52 | } 53 | }); 54 | 55 | LOG.debug("Created gauge {} for method {}.{}", metricName, targetClass.getCanonicalName(), method.getName()); 56 | } 57 | 58 | @Override 59 | public int getOrder() { 60 | return LOWEST_PRECEDENCE; 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/spring/HealthCheckBeanPostProcessor.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring; 17 | 18 | import org.slf4j.Logger; 19 | import org.slf4j.LoggerFactory; 20 | import org.springframework.beans.factory.config.BeanPostProcessor; 21 | import org.springframework.core.Ordered; 22 | 23 | import com.codahale.metrics.health.HealthCheck; 24 | import com.codahale.metrics.health.HealthCheckRegistry; 25 | 26 | class HealthCheckBeanPostProcessor implements BeanPostProcessor, Ordered { 27 | 28 | private static final Logger LOG = LoggerFactory.getLogger(HealthCheckBeanPostProcessor.class); 29 | 30 | private final HealthCheckRegistry healthChecks; 31 | 32 | public HealthCheckBeanPostProcessor(final HealthCheckRegistry healthChecks) { 33 | this.healthChecks = healthChecks; 34 | } 35 | 36 | @Override 37 | public Object postProcessBeforeInitialization(Object bean, String beanName) { 38 | return bean; 39 | } 40 | 41 | @Override 42 | public Object postProcessAfterInitialization(Object bean, String beanName) { 43 | if (bean instanceof HealthCheck) { 44 | healthChecks.register(beanName, (HealthCheck) bean); 45 | 46 | LOG.debug("Registering HealthCheck bean {}", beanName); 47 | } 48 | 49 | return bean; 50 | } 51 | 52 | @Override 53 | public int getOrder() { 54 | return LOWEST_PRECEDENCE; 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/spring/LegacyCachedGaugeAnnotationBeanPostProcessor.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring; 17 | 18 | import java.lang.reflect.Method; 19 | 20 | import org.springframework.core.Ordered; 21 | import org.springframework.util.ReflectionUtils; 22 | 23 | import com.codahale.metrics.MetricRegistry; 24 | import com.ryantenney.metrics.annotation.CachedGauge; 25 | 26 | import static com.ryantenney.metrics.spring.AnnotationFilter.INSTANCE_METHODS; 27 | 28 | @Deprecated 29 | class LegacyCachedGaugeAnnotationBeanPostProcessor extends AbstractAnnotationBeanPostProcessor implements Ordered { 30 | 31 | private static final AnnotationFilter FILTER = new AnnotationFilter(CachedGauge.class, INSTANCE_METHODS); 32 | 33 | private final MetricRegistry metrics; 34 | 35 | public LegacyCachedGaugeAnnotationBeanPostProcessor(final MetricRegistry metrics) { 36 | super(Members.METHODS, Phase.POST_INIT, FILTER); 37 | this.metrics = metrics; 38 | } 39 | 40 | @Override 41 | protected void withMethod(final Object bean, String beanName, Class targetClass, final Method method) { 42 | if (method.getParameterTypes().length > 0) { 43 | throw new IllegalStateException("Method " + method.getName() + " is annotated with @CachedGauge but requires parameters."); 44 | } 45 | 46 | final CachedGauge annotation = method.getAnnotation(CachedGauge.class); 47 | final String metricName = Util.forCachedGauge(targetClass, method, annotation); 48 | 49 | metrics.register(metricName, new com.codahale.metrics.CachedGauge(annotation.timeout(), annotation.timeoutUnit()) { 50 | @Override 51 | protected Object loadValue() { 52 | return ReflectionUtils.invokeMethod(method, bean); 53 | } 54 | }); 55 | 56 | LOG.debug("Created cached gauge {} for method {}.{}", metricName, targetClass.getCanonicalName(), method.getName()); 57 | } 58 | 59 | @Override 60 | public int getOrder() { 61 | return LOWEST_PRECEDENCE; 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/spring/LegacyCountedMethodInterceptor.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring; 17 | 18 | import java.lang.reflect.Method; 19 | 20 | import org.aopalliance.aop.Advice; 21 | import org.aopalliance.intercept.MethodInvocation; 22 | import org.springframework.aop.Pointcut; 23 | import org.springframework.aop.support.annotation.AnnotationMatchingPointcut; 24 | import org.springframework.util.ReflectionUtils.MethodFilter; 25 | 26 | import com.codahale.metrics.Counter; 27 | import com.codahale.metrics.MetricRegistry; 28 | import com.ryantenney.metrics.annotation.Counted; 29 | 30 | import static com.ryantenney.metrics.spring.AnnotationFilter.PROXYABLE_METHODS; 31 | 32 | @Deprecated 33 | class LegacyCountedMethodInterceptor extends AbstractMetricMethodInterceptor { 34 | 35 | public static final Class ANNOTATION = Counted.class; 36 | public static final Pointcut POINTCUT = new AnnotationMatchingPointcut(null, ANNOTATION); 37 | public static final MethodFilter METHOD_FILTER = new AnnotationFilter(ANNOTATION, PROXYABLE_METHODS); 38 | 39 | public LegacyCountedMethodInterceptor(final MetricRegistry metricRegistry, final Class targetClass) { 40 | super(metricRegistry, targetClass, ANNOTATION, METHOD_FILTER); 41 | } 42 | 43 | @Override 44 | protected Object invoke(MethodInvocation invocation, Counter counter, Counted annotation) throws Throwable { 45 | try { 46 | counter.inc(); 47 | return invocation.proceed(); 48 | } 49 | finally { 50 | if (!annotation.monotonic()) { 51 | counter.dec(); 52 | } 53 | } 54 | } 55 | 56 | @Override 57 | protected Counter buildMetric(MetricRegistry metricRegistry, String metricName, Counted annotation) { 58 | return metricRegistry.counter(metricName); 59 | } 60 | 61 | @Override 62 | protected String buildMetricName(Class targetClass, Method method, Counted annotation) { 63 | return Util.forCountedMethod(targetClass, method, annotation); 64 | } 65 | 66 | static AdviceFactory adviceFactory(final MetricRegistry metricRegistry) { 67 | return new AdviceFactory() { 68 | @Override 69 | public Advice getAdvice(Object bean, Class targetClass) { 70 | return new LegacyCountedMethodInterceptor(metricRegistry, targetClass); 71 | } 72 | }; 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/spring/MeteredMethodInterceptor.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring; 17 | 18 | import java.lang.reflect.Method; 19 | 20 | import org.aopalliance.aop.Advice; 21 | import org.aopalliance.intercept.MethodInvocation; 22 | import org.springframework.aop.Pointcut; 23 | import org.springframework.aop.support.annotation.AnnotationMatchingPointcut; 24 | import org.springframework.util.ReflectionUtils.MethodFilter; 25 | 26 | import com.codahale.metrics.Meter; 27 | import com.codahale.metrics.MetricRegistry; 28 | import com.codahale.metrics.annotation.Metered; 29 | 30 | import static com.ryantenney.metrics.spring.AnnotationFilter.PROXYABLE_METHODS; 31 | 32 | class MeteredMethodInterceptor extends AbstractMetricMethodInterceptor { 33 | 34 | public static final Class ANNOTATION = Metered.class; 35 | public static final Pointcut POINTCUT = new AnnotationMatchingPointcut(null, ANNOTATION); 36 | public static final MethodFilter METHOD_FILTER = new AnnotationFilter(ANNOTATION, PROXYABLE_METHODS); 37 | 38 | public MeteredMethodInterceptor(final MetricRegistry metricRegistry, final Class targetClass) { 39 | super(metricRegistry, targetClass, ANNOTATION, METHOD_FILTER); 40 | } 41 | 42 | @Override 43 | protected Object invoke(MethodInvocation invocation, Meter meter, Metered annotation) throws Throwable { 44 | meter.mark(); 45 | return invocation.proceed(); 46 | } 47 | 48 | @Override 49 | protected Meter buildMetric(MetricRegistry metricRegistry, String metricName, Metered annotation) { 50 | return metricRegistry.meter(metricName); 51 | } 52 | 53 | @Override 54 | protected String buildMetricName(Class targetClass, Method method, Metered annotation) { 55 | return Util.forMeteredMethod(targetClass, method, annotation); 56 | } 57 | 58 | static AdviceFactory adviceFactory(final MetricRegistry metricRegistry) { 59 | return new AdviceFactory() { 60 | @Override 61 | public Advice getAdvice(Object bean, Class targetClass) { 62 | return new MeteredMethodInterceptor(metricRegistry, targetClass); 63 | } 64 | }; 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/spring/MethodKey.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring; 17 | 18 | import java.lang.reflect.Method; 19 | import java.util.Arrays; 20 | 21 | class MethodKey { 22 | 23 | private final String name; 24 | private final Class returnType; 25 | private final Class[] parameterTypes; 26 | private final int hashCode; 27 | 28 | public static MethodKey forMethod(Method method) { 29 | return new MethodKey(method); 30 | } 31 | 32 | private MethodKey(Method method) { 33 | this.name = method.getName(); 34 | this.returnType = method.getReturnType(); 35 | this.parameterTypes = method.getParameterTypes(); 36 | this.hashCode = computeHashCode(); 37 | } 38 | 39 | @Override 40 | public int hashCode() { 41 | return hashCode; 42 | } 43 | 44 | private int computeHashCode() { 45 | final int prime = 31; 46 | int result = 1; 47 | result = prime * result + name.hashCode(); 48 | result = prime * result + Arrays.hashCode(parameterTypes); 49 | result = prime * result + returnType.hashCode(); 50 | return result; 51 | } 52 | 53 | @Override 54 | public boolean equals(Object obj) { 55 | if (this == obj) { 56 | return true; 57 | } 58 | if (obj == null) { 59 | return false; 60 | } 61 | if (getClass() != obj.getClass()) { 62 | return false; 63 | } 64 | final MethodKey other = (MethodKey) obj; 65 | return name.equals(other.name) && returnType.equals(other.returnType) && Arrays.equals(parameterTypes, other.parameterTypes); 66 | } 67 | 68 | @Override 69 | public String toString() { 70 | final StringBuilder sb = new StringBuilder(); 71 | sb.append(returnType.getSimpleName()); 72 | sb.append(' '); 73 | sb.append(name); 74 | sb.append('('); 75 | boolean firstParam = true; 76 | for (Class parameterType : parameterTypes) { 77 | if (firstParam) { 78 | firstParam = false; 79 | } 80 | else { 81 | sb.append(", "); 82 | } 83 | sb.append(parameterType.getSimpleName()); 84 | } 85 | sb.append(')'); 86 | return sb.toString(); 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/spring/MetricsBeanPostProcessorFactory.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring; 17 | 18 | import org.springframework.aop.framework.ProxyConfig; 19 | 20 | import com.codahale.metrics.MetricRegistry; 21 | import com.codahale.metrics.health.HealthCheckRegistry; 22 | 23 | public class MetricsBeanPostProcessorFactory { 24 | 25 | private MetricsBeanPostProcessorFactory() {} 26 | 27 | public static AdvisingBeanPostProcessor exceptionMetered(final MetricRegistry metricRegistry, final ProxyConfig proxyConfig) { 28 | return new AdvisingBeanPostProcessor(ExceptionMeteredMethodInterceptor.POINTCUT, ExceptionMeteredMethodInterceptor.adviceFactory(metricRegistry), 29 | proxyConfig); 30 | } 31 | 32 | public static AdvisingBeanPostProcessor metered(final MetricRegistry metricRegistry, final ProxyConfig proxyConfig) { 33 | return new AdvisingBeanPostProcessor(MeteredMethodInterceptor.POINTCUT, MeteredMethodInterceptor.adviceFactory(metricRegistry), proxyConfig); 34 | } 35 | 36 | public static AdvisingBeanPostProcessor timed(final MetricRegistry metricRegistry, final ProxyConfig proxyConfig) { 37 | return new AdvisingBeanPostProcessor(TimedMethodInterceptor.POINTCUT, TimedMethodInterceptor.adviceFactory(metricRegistry), proxyConfig); 38 | } 39 | 40 | public static AdvisingBeanPostProcessor counted(final MetricRegistry metricRegistry, final ProxyConfig proxyConfig) { 41 | return new AdvisingBeanPostProcessor(CountedMethodInterceptor.POINTCUT, CountedMethodInterceptor.adviceFactory(metricRegistry), proxyConfig); 42 | } 43 | 44 | public static GaugeFieldAnnotationBeanPostProcessor gaugeField(final MetricRegistry metricRegistry) { 45 | return new GaugeFieldAnnotationBeanPostProcessor(metricRegistry); 46 | } 47 | 48 | public static GaugeMethodAnnotationBeanPostProcessor gaugeMethod(final MetricRegistry metricRegistry) { 49 | return new GaugeMethodAnnotationBeanPostProcessor(metricRegistry); 50 | } 51 | 52 | public static CachedGaugeAnnotationBeanPostProcessor cachedGauge(final MetricRegistry metricRegistry) { 53 | return new CachedGaugeAnnotationBeanPostProcessor(metricRegistry); 54 | } 55 | 56 | public static MetricAnnotationBeanPostProcessor metric(final MetricRegistry metricRegistry) { 57 | return new MetricAnnotationBeanPostProcessor(metricRegistry); 58 | } 59 | 60 | public static HealthCheckBeanPostProcessor healthCheck(final HealthCheckRegistry healthRegistry) { 61 | return new HealthCheckBeanPostProcessor(healthRegistry); 62 | } 63 | 64 | @Deprecated 65 | public static AdvisingBeanPostProcessor legacyCounted(final MetricRegistry metricRegistry, final ProxyConfig proxyConfig) { 66 | return new AdvisingBeanPostProcessor(LegacyCountedMethodInterceptor.POINTCUT, LegacyCountedMethodInterceptor.adviceFactory(metricRegistry), proxyConfig); 67 | } 68 | 69 | @Deprecated 70 | public static LegacyCachedGaugeAnnotationBeanPostProcessor legacyCachedGauge(final MetricRegistry metricRegistry) { 71 | return new LegacyCachedGaugeAnnotationBeanPostProcessor(metricRegistry); 72 | } 73 | 74 | @Deprecated 75 | public static LegacyMetricAnnotationBeanPostProcessor legacyMetric(final MetricRegistry metricRegistry) { 76 | return new LegacyMetricAnnotationBeanPostProcessor(metricRegistry); 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/spring/TimedMethodInterceptor.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring; 17 | 18 | import java.lang.reflect.Method; 19 | 20 | import org.aopalliance.aop.Advice; 21 | import org.aopalliance.intercept.MethodInvocation; 22 | import org.springframework.aop.Pointcut; 23 | import org.springframework.aop.support.annotation.AnnotationMatchingPointcut; 24 | import org.springframework.core.Ordered; 25 | import org.springframework.util.ReflectionUtils.MethodFilter; 26 | 27 | import com.codahale.metrics.MetricRegistry; 28 | import com.codahale.metrics.Timer; 29 | import com.codahale.metrics.Timer.Context; 30 | import com.codahale.metrics.annotation.Timed; 31 | 32 | import static com.ryantenney.metrics.spring.AnnotationFilter.PROXYABLE_METHODS; 33 | 34 | class TimedMethodInterceptor extends AbstractMetricMethodInterceptor implements Ordered { 35 | 36 | public static final Class ANNOTATION = Timed.class; 37 | public static final Pointcut POINTCUT = new AnnotationMatchingPointcut(null, ANNOTATION); 38 | public static final MethodFilter METHOD_FILTER = new AnnotationFilter(ANNOTATION, PROXYABLE_METHODS); 39 | 40 | public TimedMethodInterceptor(final MetricRegistry metricRegistry, final Class targetClass) { 41 | super(metricRegistry, targetClass, ANNOTATION, METHOD_FILTER); 42 | } 43 | 44 | @Override 45 | protected Object invoke(MethodInvocation invocation, Timer timer, Timed annotation) throws Throwable { 46 | final Context timerCtx = timer.time(); 47 | try { 48 | return invocation.proceed(); 49 | } 50 | finally { 51 | timerCtx.close(); 52 | } 53 | } 54 | 55 | @Override 56 | protected Timer buildMetric(MetricRegistry metricRegistry, String metricName, Timed annotation) { 57 | return metricRegistry.timer(metricName); 58 | } 59 | 60 | @Override 61 | protected String buildMetricName(Class targetClass, Method method, Timed annotation) { 62 | return Util.forTimedMethod(targetClass, method, annotation); 63 | } 64 | 65 | @Override 66 | public int getOrder() { 67 | return HIGHEST_PRECEDENCE; 68 | } 69 | 70 | static AdviceFactory adviceFactory(final MetricRegistry metricRegistry) { 71 | return new AdviceFactory() { 72 | @Override 73 | public Advice getAdvice(Object bean, Class targetClass) { 74 | return new TimedMethodInterceptor(metricRegistry, targetClass); 75 | } 76 | }; 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/spring/Util.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring; 17 | 18 | import static com.codahale.metrics.MetricRegistry.name; 19 | 20 | import java.lang.reflect.Member; 21 | 22 | import com.codahale.metrics.annotation.CachedGauge; 23 | import com.codahale.metrics.annotation.Counted; 24 | import com.codahale.metrics.annotation.ExceptionMetered; 25 | import com.codahale.metrics.annotation.Gauge; 26 | import com.codahale.metrics.annotation.Metered; 27 | import com.codahale.metrics.annotation.Metric; 28 | import com.codahale.metrics.annotation.Timed; 29 | 30 | class Util { 31 | 32 | private Util() {} 33 | 34 | static String forTimedMethod(Class klass, Member member, Timed annotation) { 35 | return chooseName(annotation.name(), annotation.absolute(), klass, member); 36 | } 37 | 38 | static String forMeteredMethod(Class klass, Member member, Metered annotation) { 39 | return chooseName(annotation.name(), annotation.absolute(), klass, member); 40 | } 41 | 42 | static String forGauge(Class klass, Member member, Gauge annotation) { 43 | return chooseName(annotation.name(), annotation.absolute(), klass, member); 44 | } 45 | 46 | static String forCachedGauge(Class klass, Member member, CachedGauge annotation) { 47 | return chooseName(annotation.name(), annotation.absolute(), klass, member); 48 | } 49 | 50 | static String forExceptionMeteredMethod(Class klass, Member member, ExceptionMetered annotation) { 51 | return chooseName(annotation.name(), annotation.absolute(), klass, member, ExceptionMetered.DEFAULT_NAME_SUFFIX); 52 | } 53 | 54 | static String forCountedMethod(Class klass, Member member, Counted annotation) { 55 | return chooseName(annotation.name(), annotation.absolute(), klass, member); 56 | } 57 | 58 | static String forMetricField(Class klass, Member member, Metric annotation) { 59 | return chooseName(annotation.name(), annotation.absolute(), klass, member); 60 | } 61 | 62 | @Deprecated 63 | static String forCachedGauge(Class klass, Member member, com.ryantenney.metrics.annotation.CachedGauge annotation) { 64 | return chooseName(annotation.name(), annotation.absolute(), klass, member); 65 | } 66 | 67 | @Deprecated 68 | static String forCountedMethod(Class klass, Member member, com.ryantenney.metrics.annotation.Counted annotation) { 69 | return chooseName(annotation.name(), annotation.absolute(), klass, member); 70 | } 71 | 72 | @Deprecated 73 | static String forMetricField(Class klass, Member member, com.ryantenney.metrics.annotation.Metric annotation) { 74 | return chooseName(annotation.name(), annotation.absolute(), klass, member); 75 | } 76 | 77 | static String chooseName(String explicitName, boolean absolute, Class klass, Member member, String... suffixes) { 78 | if (explicitName != null && !explicitName.isEmpty()) { 79 | if (absolute) { 80 | return explicitName; 81 | } 82 | return name(klass.getCanonicalName(), explicitName); 83 | } 84 | return name(name(klass.getCanonicalName(), member.getName()), suffixes); 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/spring/config/HealthCheckRegistryBeanDefinitionParser.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring.config; 17 | 18 | import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; 19 | import org.w3c.dom.Element; 20 | 21 | import com.codahale.metrics.health.HealthCheckRegistry; 22 | 23 | class HealthCheckRegistryBeanDefinitionParser extends AbstractSingleBeanDefinitionParser { 24 | 25 | @Override 26 | protected Class getBeanClass(Element element) { 27 | return HealthCheckRegistry.class; 28 | } 29 | 30 | @Override 31 | protected boolean shouldGenerateIdAsFallback() { 32 | return true; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/spring/config/MetricRegistryBeanDefinitionParser.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring.config; 17 | 18 | import static org.springframework.beans.factory.config.BeanDefinition.ROLE_APPLICATION; 19 | 20 | import org.springframework.beans.factory.support.AbstractBeanDefinition; 21 | import org.springframework.beans.factory.support.BeanDefinitionBuilder; 22 | import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser; 23 | import org.springframework.beans.factory.xml.ParserContext; 24 | import org.springframework.util.StringUtils; 25 | import org.w3c.dom.Element; 26 | 27 | import com.codahale.metrics.MetricRegistry; 28 | import com.codahale.metrics.SharedMetricRegistries; 29 | 30 | /** 31 | * Has the side effect of registering 'name' as aliases 32 | */ 33 | class MetricRegistryBeanDefinitionParser extends AbstractBeanDefinitionParser { 34 | 35 | @Override 36 | protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) { 37 | final Object source = parserContext.extractSource(element); 38 | final String name = element.getAttribute("name"); 39 | if (StringUtils.hasText(name)) { 40 | final BeanDefinitionBuilder beanDefBuilder = build(SharedMetricRegistries.class, source); 41 | beanDefBuilder.setFactoryMethod("getOrCreate"); 42 | beanDefBuilder.addConstructorArgValue(name); 43 | return beanDefBuilder.getBeanDefinition(); 44 | } 45 | else { 46 | return build(MetricRegistry.class, source).getBeanDefinition(); 47 | } 48 | } 49 | 50 | @Override 51 | protected boolean shouldGenerateIdAsFallback() { 52 | return true; 53 | } 54 | 55 | private BeanDefinitionBuilder build(Class klazz, Object source) { 56 | final BeanDefinitionBuilder beanDefBuilder = BeanDefinitionBuilder.rootBeanDefinition(klazz); 57 | beanDefBuilder.setRole(ROLE_APPLICATION); 58 | beanDefBuilder.getRawBeanDefinition().setSource(source); 59 | return beanDefBuilder; 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/spring/config/MetricsNamespaceHandler.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring.config; 17 | 18 | import org.springframework.beans.factory.xml.NamespaceHandlerSupport; 19 | 20 | class MetricsNamespaceHandler extends NamespaceHandlerSupport { 21 | 22 | public static final String METRICS_NAMESPACE = "http://www.ryantenney.com/schema/metrics"; 23 | 24 | @Override 25 | public void init() { 26 | registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser()); 27 | registerBeanDefinitionParser("metric-registry", new MetricRegistryBeanDefinitionParser()); 28 | registerBeanDefinitionParser("health-check-registry", new HealthCheckRegistryBeanDefinitionParser()); 29 | registerBeanDefinitionParser("reporter", new ReporterBeanDefinitionParser()); 30 | registerBeanDefinitionParser("register", new RegisterMetricBeanDefinitionParser()); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/spring/config/ReporterBeanDefinitionParser.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring.config; 17 | 18 | import java.util.ServiceConfigurationError; 19 | import java.util.ServiceLoader; 20 | 21 | import org.springframework.beans.factory.support.AbstractBeanDefinition; 22 | import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser; 23 | import org.springframework.beans.factory.xml.ParserContext; 24 | import org.springframework.util.StringUtils; 25 | import org.w3c.dom.Element; 26 | 27 | import com.ryantenney.metrics.spring.reporter.ReporterElementParser; 28 | 29 | /** 30 | * Has the side effect of registering 'name' as aliases 31 | */ 32 | class ReporterBeanDefinitionParser extends AbstractBeanDefinitionParser { 33 | 34 | private final ServiceLoader reporterElementParserLoader = ServiceLoader.load(ReporterElementParser.class); 35 | 36 | @Override 37 | protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) { 38 | final String metricRegistryRef = element.getAttribute("metric-registry"); 39 | if (!StringUtils.hasText(metricRegistryRef)) { 40 | parserContext.getReaderContext().error("Metric-registry id required for element '" + element.getLocalName() + "'", element); 41 | return null; 42 | } 43 | 44 | final String type = element.getAttribute("type"); 45 | if (!StringUtils.hasText(type)) { 46 | parserContext.getReaderContext().error("Type required for element '" + element.getLocalName() + "'", element); 47 | return null; 48 | } 49 | 50 | try { 51 | for (ReporterElementParser reporterElementParser : reporterElementParserLoader) { 52 | if (type.equals(reporterElementParser.getType())) { 53 | return reporterElementParser.parseReporter(element, parserContext); 54 | } 55 | } 56 | } 57 | catch (ServiceConfigurationError ex) { 58 | parserContext.getReaderContext().error("Error loading ReporterElementParsers", element, ex); 59 | return null; 60 | } 61 | 62 | // Unable to locate a ReporterElementParser for the given type. 63 | // Check to see if we're in Spring Tool Suite and if so, we won't throw an error. 64 | if (!isInvokedBySts()) { 65 | parserContext.getReaderContext().error("No ReporterElementParser found for reporter type '" + type + "'", element); 66 | } 67 | 68 | return null; 69 | } 70 | 71 | @Override 72 | protected boolean shouldGenerateIdAsFallback() { 73 | return true; 74 | } 75 | 76 | private boolean isInvokedBySts() { 77 | try { 78 | StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); 79 | for (StackTraceElement stackTraceElement : stackTrace) { 80 | if (stackTraceElement.getClassName().startsWith("org.springframework.ide.eclipse.")) { 81 | return true; 82 | } 83 | } 84 | } 85 | catch (SecurityException ignored) { 86 | } 87 | 88 | return false; 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/spring/config/annotation/DelegatingMetricsConfiguration.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring.config.annotation; 17 | 18 | import java.util.List; 19 | 20 | import org.springframework.beans.factory.annotation.Autowired; 21 | import org.springframework.context.annotation.Bean; 22 | import org.springframework.context.annotation.Configuration; 23 | 24 | import com.codahale.metrics.MetricRegistry; 25 | import com.codahale.metrics.health.HealthCheckRegistry; 26 | 27 | /** 28 | * This is the class imported by {@link EnableMetrics @EnableMetrics}. 29 | * 30 | * @see MetricsConfigurer 31 | * @see MetricsConfigurationSupport 32 | * @author Ryan Tenney 33 | * @since 3.0 34 | */ 35 | @Configuration 36 | public class DelegatingMetricsConfiguration extends MetricsConfigurationSupport implements MetricsConfigurer { 37 | 38 | private MetricsConfigurerComposite delegates = new MetricsConfigurerComposite(); 39 | 40 | private MetricRegistry metricRegistry; 41 | private HealthCheckRegistry healthCheckRegistry; 42 | 43 | @Autowired(required = false) 44 | public void setMetricsConfigurers(final List configurers) { 45 | if (configurers != null) { 46 | this.delegates.addMetricsConfigurers(configurers); 47 | } 48 | } 49 | 50 | @Override 51 | public void configureReporters(final MetricRegistry metricRegistry) { 52 | this.delegates.configureReporters(metricRegistry); 53 | } 54 | 55 | @Bean 56 | @Override 57 | public MetricRegistry getMetricRegistry() { 58 | if (this.metricRegistry == null) { 59 | this.metricRegistry = this.delegates.getMetricRegistry(); 60 | this.configureReporters(this.metricRegistry); 61 | } 62 | return this.metricRegistry; 63 | } 64 | 65 | @Bean 66 | @Override 67 | public HealthCheckRegistry getHealthCheckRegistry() { 68 | if (this.healthCheckRegistry == null) { 69 | this.healthCheckRegistry = this.delegates.getHealthCheckRegistry(); 70 | } 71 | return this.healthCheckRegistry; 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/spring/config/annotation/EnableMetrics.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring.config.annotation; 17 | 18 | import java.lang.annotation.Documented; 19 | import java.lang.annotation.ElementType; 20 | import java.lang.annotation.Retention; 21 | import java.lang.annotation.RetentionPolicy; 22 | import java.lang.annotation.Target; 23 | 24 | import org.springframework.context.annotation.Import; 25 | 26 | /** 27 | * Add this annotation to an {@code @Configuration} class to have the Spring MVC 28 | * configuration defined in {@link MetricsConfigurationSupport} imported. 29 | * 30 | * @see MetricsConfigurer 31 | * @see MetricsConfigurerAdapter 32 | * 33 | * @author Ryan Tenney 34 | * @since 3.0 35 | */ 36 | @Target(ElementType.TYPE) 37 | @Retention(RetentionPolicy.RUNTIME) 38 | @Documented 39 | @Import(DelegatingMetricsConfiguration.class) 40 | public @interface EnableMetrics { 41 | 42 | /** 43 | * Set whether the proxy should be exposed by the AOP framework as a 44 | * ThreadLocal for retrieval via the AopContext class. This is useful 45 | * if an advised object needs to call another advised method on itself. 46 | * (If it uses this, the invocation will not be advised). 47 | *

Default is "false", in order to avoid unnecessary extra interception. 48 | * This means that no guarantees are provided that AopContext access will 49 | * work consistently within any method of the advised object. 50 | */ 51 | boolean exposeProxy() default false; 52 | 53 | /** 54 | * Set whether to proxy the target class directly, instead of just proxying 55 | * specific interfaces. Default is "false". 56 | *

Set this to "true" to force proxying for the TargetSource's exposed 57 | * target class. If that target class is an interface, a JDK proxy will be 58 | * created for the given interface. If that target class is any other class, 59 | * a CGLIB proxy will be created for the given class. 60 | *

Note: Depending on the configuration of the concrete proxy factory, 61 | * the proxy-target-class behavior will also be applied if no interfaces 62 | * have been specified (and no interface autodetection is activated). 63 | * @see org.springframework.aop.TargetSource#getTargetClass() 64 | */ 65 | boolean proxyTargetClass() default false; 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/spring/config/annotation/MetricsConfigurer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring.config.annotation; 17 | 18 | import com.codahale.metrics.MetricRegistry; 19 | import com.codahale.metrics.health.HealthCheckRegistry; 20 | 21 | /** 22 | * Defines callback methods to customize the Java-based configuration 23 | * for Spring Metrics enabled via {@link EnableMetrics @EnableMetrics}. 24 | * 25 | * @see EnableMetrics 26 | * @author Ryan Tenney 27 | * @since 3.0 28 | */ 29 | public interface MetricsConfigurer { 30 | 31 | /** 32 | * Configure reporters. 33 | * @param metricRegistry 34 | */ 35 | void configureReporters(MetricRegistry metricRegistry); 36 | 37 | /** 38 | * Override this method to provide a custom {@code MetricRegistry}. 39 | * @return 40 | */ 41 | MetricRegistry getMetricRegistry(); 42 | 43 | /** 44 | * Override this method to provide a custom {@code HealthCheckRegistry}. 45 | * @return 46 | */ 47 | HealthCheckRegistry getHealthCheckRegistry(); 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/spring/config/annotation/MetricsConfigurerAdapter.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring.config.annotation; 17 | 18 | import java.io.Closeable; 19 | import java.util.HashSet; 20 | import java.util.Set; 21 | 22 | import org.slf4j.Logger; 23 | import org.slf4j.LoggerFactory; 24 | import org.springframework.beans.factory.DisposableBean; 25 | 26 | import com.codahale.metrics.MetricRegistry; 27 | import com.codahale.metrics.health.HealthCheckRegistry; 28 | 29 | /** 30 | * An implementation of {@link MetricsConfigurer} with empty methods allowing 31 | * sub-classes to override only the methods they're interested in. 32 | * 33 | * @see EnableMetrics 34 | * @see MetricsConfigurer 35 | * @author Ryan Tenney 36 | * @since 3.0 37 | */ 38 | public abstract class MetricsConfigurerAdapter implements MetricsConfigurer, DisposableBean { 39 | 40 | private static final Logger LOG = LoggerFactory.getLogger(MetricsConfigurerAdapter.class); 41 | 42 | private Set reporters; 43 | 44 | /** 45 | * {@inheritDoc} 46 | *

This implementation is empty. 47 | */ 48 | @Override 49 | public void configureReporters(MetricRegistry metricRegistry) {} 50 | 51 | /** 52 | * {@inheritDoc} 53 | *

This implementation returns {@code null}. 54 | */ 55 | @Override 56 | public MetricRegistry getMetricRegistry() { 57 | return null; 58 | } 59 | 60 | /** 61 | * {@inheritDoc} 62 | *

This implementation returns {@code null}. 63 | */ 64 | @Override 65 | public HealthCheckRegistry getHealthCheckRegistry() { 66 | return null; 67 | } 68 | 69 | /** 70 | * Called when the Spring context is closed, this method stops reporters. 71 | */ 72 | @Override 73 | public void destroy() throws Exception { 74 | if (this.reporters != null) { 75 | for (Closeable reporter : this.reporters) { 76 | try { 77 | reporter.close(); 78 | } 79 | catch (Exception ex) { 80 | LOG.warn("Problem stopping reporter", ex); 81 | } 82 | } 83 | } 84 | } 85 | 86 | /** 87 | * Registers a reporter for destruction on Spring context close 88 | * @param reporter a reporter which implements Closeable 89 | * @return the reporter 90 | */ 91 | protected R registerReporter(final R reporter) { 92 | if (this.reporters == null) { 93 | this.reporters = new HashSet(); 94 | } 95 | this.reporters.add(reporter); 96 | return reporter; 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/spring/config/annotation/MetricsConfigurerComposite.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring.config.annotation; 17 | 18 | import java.util.ArrayList; 19 | import java.util.Collection; 20 | import java.util.List; 21 | 22 | import com.codahale.metrics.MetricRegistry; 23 | import com.codahale.metrics.health.HealthCheckRegistry; 24 | 25 | /** 26 | * A {@link MetricsConfigurer} implementation that delegates to other {@link MetricsConfigurer} instances. 27 | * 28 | * @author Ryan Tenney 29 | * @since 3.0 30 | */ 31 | public class MetricsConfigurerComposite implements MetricsConfigurer { 32 | 33 | private final List configurers = new ArrayList(); 34 | 35 | public void addMetricsConfigurers(final Collection configurers) { 36 | if (configurers != null) { 37 | this.configurers.addAll(configurers); 38 | } 39 | } 40 | 41 | @Override 42 | public void configureReporters(final MetricRegistry metricRegistry) { 43 | for (MetricsConfigurer configurer : this.configurers) { 44 | configurer.configureReporters(metricRegistry); 45 | } 46 | } 47 | 48 | @Override 49 | public MetricRegistry getMetricRegistry() { 50 | final List candidates = new ArrayList(); 51 | for (MetricsConfigurer configurer : this.configurers) { 52 | final MetricRegistry metricRegistry = configurer.getMetricRegistry(); 53 | if (metricRegistry != null) { 54 | candidates.add(metricRegistry); 55 | } 56 | } 57 | MetricRegistry instance = selectSingleInstance(candidates, MetricRegistry.class); 58 | if (instance == null) { 59 | instance = new MetricRegistry(); 60 | } 61 | return instance; 62 | } 63 | 64 | @Override 65 | public HealthCheckRegistry getHealthCheckRegistry() { 66 | final List candidates = new ArrayList(); 67 | for (MetricsConfigurer configurer : this.configurers) { 68 | final HealthCheckRegistry healthCheckRegistry = configurer.getHealthCheckRegistry(); 69 | if (healthCheckRegistry != null) { 70 | candidates.add(healthCheckRegistry); 71 | } 72 | } 73 | HealthCheckRegistry instance = selectSingleInstance(candidates, HealthCheckRegistry.class); 74 | if (instance == null) { 75 | instance = new HealthCheckRegistry(); 76 | } 77 | return instance; 78 | } 79 | 80 | private T selectSingleInstance(final List instances, final Class instanceType) { 81 | if (instances.size() > 1) { 82 | throw new IllegalStateException("Only one [" + instanceType + "] was expected but multiple instances were provided: " + instances); 83 | } 84 | else if (instances.size() == 1) { 85 | return instances.get(0); 86 | } 87 | else { 88 | return null; 89 | } 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/spring/reporter/AbstractScheduledReporterFactoryBean.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring.reporter; 17 | 18 | import com.codahale.metrics.ScheduledReporter; 19 | import org.springframework.beans.factory.DisposableBean; 20 | import org.springframework.context.SmartLifecycle; 21 | 22 | import java.util.concurrent.TimeUnit; 23 | import java.util.regex.Matcher; 24 | import java.util.regex.Pattern; 25 | 26 | public abstract class AbstractScheduledReporterFactoryBean extends AbstractReporterFactoryBean implements SmartLifecycle, 27 | DisposableBean { 28 | 29 | private static final Pattern DURATION_STRING_PATTERN = Pattern.compile("^(\\d+)\\s?(ns|us|ms|s|m|h|d)?$"); 30 | 31 | private boolean running; 32 | 33 | @Override 34 | public void start() { 35 | if (isEnabled() && !isRunning()) { 36 | getObject().start(getPeriod(), TimeUnit.NANOSECONDS); 37 | running = true; 38 | } 39 | } 40 | 41 | @Override 42 | public void stop() { 43 | if (isEnabled() && isRunning()) { 44 | getObject().stop(); 45 | running = false; 46 | } 47 | } 48 | 49 | @Override 50 | public boolean isRunning() { 51 | return running; 52 | } 53 | 54 | @Override 55 | public void destroy() throws Exception { 56 | stop(); 57 | } 58 | 59 | protected abstract long getPeriod(); 60 | 61 | /** 62 | * Parses and converts to nanoseconds a string representing 63 | * a duration, ie: 500ms, 30s, 5m, 1h, etc 64 | * @param duration a string representing a duration 65 | * @return the duration in nanoseconds 66 | */ 67 | protected long convertDurationString(String duration) { 68 | final Matcher m = DURATION_STRING_PATTERN.matcher(duration); 69 | if (!m.matches()) { 70 | throw new IllegalArgumentException("Invalid duration string format"); 71 | } 72 | 73 | final long sourceDuration = Long.parseLong(m.group(1)); 74 | final String sourceUnitString = m.group(2); 75 | final TimeUnit sourceUnit; 76 | if ("ns".equalsIgnoreCase(sourceUnitString)) { 77 | sourceUnit = TimeUnit.NANOSECONDS; 78 | } 79 | else if ("us".equalsIgnoreCase(sourceUnitString)) { 80 | sourceUnit = TimeUnit.MICROSECONDS; 81 | } 82 | else if ("ms".equalsIgnoreCase(sourceUnitString)) { 83 | sourceUnit = TimeUnit.MILLISECONDS; 84 | } 85 | else if ("s".equalsIgnoreCase(sourceUnitString)) { 86 | sourceUnit = TimeUnit.SECONDS; 87 | } 88 | else if ("m".equalsIgnoreCase(sourceUnitString)) { 89 | sourceUnit = TimeUnit.MINUTES; 90 | } 91 | else if ("h".equalsIgnoreCase(sourceUnitString)) { 92 | sourceUnit = TimeUnit.HOURS; 93 | } 94 | else if ("d".equalsIgnoreCase(sourceUnitString)) { 95 | sourceUnit = TimeUnit.DAYS; 96 | } 97 | else { 98 | sourceUnit = TimeUnit.MILLISECONDS; 99 | } 100 | 101 | return sourceUnit.toNanos(sourceDuration); 102 | } 103 | 104 | @Override 105 | public int getPhase() { 106 | return 0; 107 | } 108 | 109 | @Override 110 | public void stop(Runnable callback) { 111 | stop(); 112 | callback.run(); 113 | } 114 | 115 | @Override 116 | public boolean isAutoStartup() { 117 | return true; 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/spring/reporter/BasicMetricPrefixSupplier.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring.reporter; 17 | 18 | public class BasicMetricPrefixSupplier implements MetricPrefixSupplier { 19 | 20 | private final String prefix; 21 | 22 | public BasicMetricPrefixSupplier(final String prefix) { 23 | this.prefix = prefix; 24 | } 25 | 26 | @Override 27 | public String getPrefix() { 28 | return prefix; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/spring/reporter/ConsoleReporterElementParser.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring.reporter; 17 | 18 | import static com.ryantenney.metrics.spring.reporter.ConsoleReporterFactoryBean.*; 19 | 20 | public class ConsoleReporterElementParser extends AbstractReporterElementParser { 21 | 22 | private static final String LOCALE_STRING_REGEX = "^[a-z]{2}(_[A-Z]{2})?$"; 23 | 24 | @Override 25 | public String getType() { 26 | return "console"; 27 | } 28 | 29 | @Override 30 | protected Class getBeanClass() { 31 | return ConsoleReporterFactoryBean.class; 32 | } 33 | 34 | @Override 35 | protected void validate(ValidationContext c) { 36 | c.require(PERIOD, DURATION_STRING_REGEX, "Period is required and must be in the form '\\d+(ns|us|ms|s|m|h|d)'"); 37 | 38 | c.optional(CLOCK_REF); 39 | c.optional(OUTPUT_REF); 40 | 41 | c.optional(LOCALE, LOCALE_STRING_REGEX, "Locale must be in the proper format"); 42 | c.optional(TIMEZONE); // Difficult to validate, if invalid will fall back to GMT 43 | 44 | c.optional(RATE_UNIT, TIMEUNIT_STRING_REGEX, "Rate unit must be one of the enum constants from java.util.concurrent.TimeUnit"); 45 | c.optional(DURATION_UNIT, TIMEUNIT_STRING_REGEX, "Duration unit must be one of the enum constants from java.util.concurrent.TimeUnit"); 46 | 47 | c.optional(FILTER_PATTERN); 48 | c.optional(FILTER_REF); 49 | if (c.has(FILTER_PATTERN) && c.has(FILTER_REF)) { 50 | c.reject(FILTER_REF, "Reporter element must not specify both the 'filter' and 'filter-ref' attributes"); 51 | } 52 | 53 | c.rejectUnmatchedProperties(); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/spring/reporter/ConsoleReporterFactoryBean.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring.reporter; 17 | 18 | import java.io.PrintStream; 19 | import java.util.Locale; 20 | import java.util.TimeZone; 21 | import java.util.concurrent.TimeUnit; 22 | 23 | import com.codahale.metrics.Clock; 24 | import com.codahale.metrics.ConsoleReporter; 25 | 26 | public class ConsoleReporterFactoryBean extends AbstractScheduledReporterFactoryBean { 27 | 28 | // Required 29 | public static final String PERIOD = "period"; 30 | 31 | // Optional 32 | public static final String CLOCK_REF = "clock-ref"; 33 | public static final String OUTPUT_REF = "output-ref"; 34 | public static final String LOCALE = "locale"; 35 | public static final String TIMEZONE = "timezone"; 36 | public static final String DURATION_UNIT = "duration-unit"; 37 | public static final String RATE_UNIT = "rate-unit"; 38 | 39 | @Override 40 | public Class getObjectType() { 41 | return ConsoleReporter.class; 42 | } 43 | 44 | @Override 45 | protected ConsoleReporter createInstance() { 46 | final ConsoleReporter.Builder reporter = ConsoleReporter.forRegistry(getMetricRegistry()); 47 | 48 | if (hasProperty(DURATION_UNIT)) { 49 | reporter.convertDurationsTo(getProperty(DURATION_UNIT, TimeUnit.class)); 50 | } 51 | 52 | if (hasProperty(RATE_UNIT)) { 53 | reporter.convertRatesTo(getProperty(RATE_UNIT, TimeUnit.class)); 54 | } 55 | 56 | reporter.filter(getMetricFilter()); 57 | 58 | if (hasProperty(CLOCK_REF)) { 59 | reporter.withClock(getPropertyRef(CLOCK_REF, Clock.class)); 60 | } 61 | 62 | if (hasProperty(OUTPUT_REF)) { 63 | reporter.outputTo(getPropertyRef(OUTPUT_REF, PrintStream.class)); 64 | } 65 | 66 | if (hasProperty(LOCALE)) { 67 | reporter.formattedFor(parseLocale(getProperty(LOCALE))); 68 | } 69 | 70 | if (hasProperty(TIMEZONE)) { 71 | reporter.formattedFor(TimeZone.getTimeZone(getProperty(TIMEZONE))); 72 | } 73 | 74 | return reporter.build(); 75 | } 76 | 77 | @Override 78 | protected long getPeriod() { 79 | return convertDurationString(getProperty(PERIOD)); 80 | } 81 | 82 | protected Locale parseLocale(String localeString) { 83 | final int underscore = localeString.indexOf('_'); 84 | if (underscore == -1) { 85 | return new Locale(localeString); 86 | } 87 | else { 88 | return new Locale(localeString.substring(0, underscore), localeString.substring(underscore + 1)); 89 | } 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/spring/reporter/CsvReporterElementParser.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring.reporter; 17 | 18 | import static com.ryantenney.metrics.spring.reporter.CsvReporterFactoryBean.*; 19 | 20 | public class CsvReporterElementParser extends AbstractReporterElementParser { 21 | 22 | private static final String LOCALE_STRING_REGEX = "^[a-z]{2}(_[A-Z]{2})?$"; 23 | 24 | @Override 25 | public String getType() { 26 | return "csv"; 27 | } 28 | 29 | @Override 30 | protected Class getBeanClass() { 31 | return CsvReporterFactoryBean.class; 32 | } 33 | 34 | @Override 35 | protected void validate(ValidationContext c) { 36 | c.require(PERIOD, DURATION_STRING_REGEX, "Period is required and must be in the form '\\d+(ns|us|ms|s|m|h|d)'"); 37 | c.require(DIRECTORY); 38 | 39 | c.optional(CLOCK_REF); 40 | 41 | c.optional(LOCALE, LOCALE_STRING_REGEX, "Locale must be in the proper format"); 42 | 43 | c.optional(RATE_UNIT, TIMEUNIT_STRING_REGEX, "Rate unit must be one of the enum constants from java.util.concurrent.TimeUnit"); 44 | c.optional(DURATION_UNIT, TIMEUNIT_STRING_REGEX, "Duration unit must be one of the enum constants from java.util.concurrent.TimeUnit"); 45 | 46 | c.optional(FILTER_PATTERN); 47 | c.optional(FILTER_REF); 48 | if (c.has(FILTER_PATTERN) && c.has(FILTER_REF)) { 49 | c.reject(FILTER_REF, "Reporter element must not specify both the 'filter' and 'filter-ref' attributes"); 50 | } 51 | 52 | c.rejectUnmatchedProperties(); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/spring/reporter/CsvReporterFactoryBean.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring.reporter; 17 | 18 | import java.io.File; 19 | import java.util.Locale; 20 | import java.util.concurrent.TimeUnit; 21 | 22 | import com.codahale.metrics.Clock; 23 | import com.codahale.metrics.CsvReporter; 24 | 25 | public class CsvReporterFactoryBean extends AbstractScheduledReporterFactoryBean { 26 | 27 | // Required 28 | public static final String PERIOD = "period"; 29 | 30 | // Optional 31 | public static final String CLOCK_REF = "clock-ref"; 32 | public static final String DIRECTORY = "directory"; 33 | public static final String LOCALE = "locale"; 34 | public static final String DURATION_UNIT = "duration-unit"; 35 | public static final String RATE_UNIT = "rate-unit"; 36 | 37 | @Override 38 | public Class getObjectType() { 39 | return CsvReporter.class; 40 | } 41 | 42 | @Override 43 | protected CsvReporter createInstance() { 44 | final CsvReporter.Builder reporter = CsvReporter.forRegistry(getMetricRegistry()); 45 | 46 | if (hasProperty(DURATION_UNIT)) { 47 | reporter.convertDurationsTo(getProperty(DURATION_UNIT, TimeUnit.class)); 48 | } 49 | 50 | if (hasProperty(RATE_UNIT)) { 51 | reporter.convertRatesTo(getProperty(RATE_UNIT, TimeUnit.class)); 52 | } 53 | 54 | reporter.filter(getMetricFilter()); 55 | 56 | if (hasProperty(CLOCK_REF)) { 57 | reporter.withClock(getPropertyRef(CLOCK_REF, Clock.class)); 58 | } 59 | 60 | if (hasProperty(LOCALE)) { 61 | reporter.formatFor(parseLocale(getProperty(LOCALE))); 62 | } 63 | 64 | File dir = new File(getProperty(DIRECTORY)); 65 | if (!dir.mkdirs() && !dir.isDirectory()) { 66 | throw new IllegalArgumentException("Directory doesn't exist or couldn't be created"); 67 | } 68 | 69 | return reporter.build(dir); 70 | } 71 | 72 | @Override 73 | protected long getPeriod() { 74 | return convertDurationString(getProperty(PERIOD)); 75 | } 76 | 77 | protected Locale parseLocale(String localeString) { 78 | final int underscore = localeString.indexOf('_'); 79 | if (underscore == -1) { 80 | return new Locale(localeString); 81 | } 82 | else { 83 | return new Locale(localeString.substring(0, underscore), localeString.substring(underscore + 1)); 84 | } 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/spring/reporter/DatadogReporterElementParser.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring.reporter; 17 | 18 | import static com.ryantenney.metrics.spring.reporter.DatadogReporterFactoryBean.*; 19 | 20 | public class DatadogReporterElementParser extends AbstractReporterElementParser { 21 | 22 | @Override 23 | public String getType() { 24 | return "datadog"; 25 | } 26 | 27 | @Override 28 | protected Class getBeanClass() { 29 | return DatadogReporterFactoryBean.class; 30 | } 31 | 32 | @Override 33 | protected void validate(ValidationContext c) { 34 | // Required 35 | c.require(TRANSPORT, "^http|udp|statsd$", "Transport must be one of: http, udp, statsd"); 36 | c.require(PERIOD, DURATION_STRING_REGEX, "Period is required and must be in the form '\\d+(ns|us|ms|s|m|h|d)'"); 37 | 38 | if ("http".equals(c.get(TRANSPORT))) { 39 | c.require(API_KEY); 40 | } 41 | 42 | // HTTP only properties 43 | c.optional(CONNECT_TIMEOUT, DURATION_STRING_REGEX, "Connect timeout must be in the form '\\d+(ns|us|ms|s|m|h|d)'"); 44 | c.optional(SOCKET_TIMEOUT, DURATION_STRING_REGEX, "Socket timeout must be in the form '\\d+(ns|us|ms|s|m|h|d)'"); 45 | 46 | // UDP only properties 47 | c.optional(STATSD_HOST); 48 | c.optional(STATSD_PORT, PORT_NUMBER_REGEX, "Port number must be an integer between 1-65536"); 49 | c.optional(STATSD_PREFIX); 50 | 51 | // All 52 | c.optional(HOST); 53 | c.optional(EC2_HOST); 54 | c.optional(EXPANSION); 55 | c.optional(TAGS); 56 | c.optional(DYNAMIC_TAG_CALLBACK_REF); 57 | c.optional(METRIC_NAME_FORMATTER_REF); 58 | 59 | c.optional(PREFIX); 60 | c.optional(CLOCK_REF); 61 | 62 | c.optional(RATE_UNIT, TIMEUNIT_STRING_REGEX, "Rate unit must be one of the enum constants from java.util.concurrent.TimeUnit"); 63 | c.optional(DURATION_UNIT, TIMEUNIT_STRING_REGEX, "Duration unit must be one of the enum constants from java.util.concurrent.TimeUnit"); 64 | 65 | c.optional(FILTER_PATTERN); 66 | c.optional(FILTER_REF); 67 | if (c.has(FILTER_PATTERN) && c.has(FILTER_REF)) { 68 | c.reject(FILTER_REF, "Reporter element must not specify both the 'filter' and 'filter-ref' attributes"); 69 | } 70 | 71 | c.rejectUnmatchedProperties(); 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/spring/reporter/ElasticSearchReporterElementParser.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring.reporter; 17 | 18 | import static com.ryantenney.metrics.spring.reporter.AbstractReporterFactoryBean.*; 19 | 20 | /** 21 | * Configuration of Metrics reporter using ES. 22 | * 23 | * @author getBeanClass() { 40 | return ElasticSearchReporterFactoryBean.class; 41 | } 42 | 43 | protected void validate(ValidationContext c) { 44 | c.require(HOSTS); 45 | c.require(PERIOD, DURATION_STRING_REGEX, "Period is required and must be in the form '\\d+(ns|us|ms|s|m|h|d)'"); 46 | 47 | c.optional(FILTER_PATTERN); 48 | c.optional(FILTER_REF); 49 | if (c.has(FILTER_PATTERN) && c.has(FILTER_REF)) { 50 | c.reject(FILTER_REF, "Reporter element must not specify both the 'filter' and 'filter-ref' attributes"); 51 | } 52 | c.optional(PREFIX); 53 | c.optional(PREFIX_SUPPLIER_REF); 54 | c.optional(ADDITIONAL_FIELDS_REF); 55 | 56 | c.rejectUnmatchedProperties(); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/spring/reporter/ElasticSearchReporterFactoryBean.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring.reporter; 17 | 18 | import org.elasticsearch.metrics.ElasticsearchReporter; 19 | 20 | import java.util.Map; 21 | import java.util.concurrent.TimeUnit; 22 | 23 | 24 | /** 25 | * Configuration of Metrics reporter using ES. 26 | * 27 | * @author getBeanClass() { 31 | return GangliaReporterFactoryBean.class; 32 | } 33 | 34 | @Override 35 | protected void validate(ValidationContext c) { 36 | c.require(GROUP); 37 | c.require(PORT, PORT_NUMBER_REGEX, "Port number is required and must be between 1-65536"); 38 | c.require(UDP_MODE); 39 | c.require(PERIOD, DURATION_STRING_REGEX, "Period is required and must be in the form '\\d+(ns|us|ms|s|m|h|d)'"); 40 | c.require(TTL, INTEGER_REGEX); 41 | 42 | c.optional(SPOOF); 43 | c.optional(PROTOCOL, "^v?3\\.[01]$", "Protocol version must be 'v3.0' or 'v3.1'"); 44 | c.optional(DMAX, INTEGER_REGEX); 45 | c.optional(TMAX, INTEGER_REGEX); 46 | 47 | c.optional(RATE_UNIT, TIMEUNIT_STRING_REGEX, "Rate unit must be one of the enum constants from java.util.concurrent.TimeUnit"); 48 | c.optional(DURATION_UNIT, TIMEUNIT_STRING_REGEX, "Duration unit must be one of the enum constants from java.util.concurrent.TimeUnit"); 49 | 50 | c.optional(UUID, UUID_STRING_REGEX); 51 | 52 | c.optional(PREFIX); 53 | c.optional(PREFIX_SUPPLIER_REF); 54 | if (c.has(PREFIX) && c.has(PREFIX_SUPPLIER_REF)) { 55 | c.reject(PREFIX_SUPPLIER_REF, "Reporter element must not specify both the 'prefix' and 'prefix-supplier-ref' attributes"); 56 | } 57 | 58 | c.optional(FILTER_PATTERN); 59 | c.optional(FILTER_REF); 60 | if (c.has(FILTER_PATTERN) && c.has(FILTER_REF)) { 61 | c.reject(FILTER_REF, "Reporter element must not specify both the 'filter' and 'filter-ref' attributes"); 62 | } 63 | 64 | c.rejectUnmatchedProperties(); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/spring/reporter/GangliaReporterFactoryBean.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring.reporter; 17 | 18 | import info.ganglia.gmetric4j.gmetric.GMetric; 19 | import info.ganglia.gmetric4j.gmetric.GMetric.UDPAddressingMode; 20 | 21 | import java.util.concurrent.TimeUnit; 22 | 23 | import com.codahale.metrics.ganglia.GangliaReporter; 24 | 25 | public class GangliaReporterFactoryBean extends AbstractScheduledReporterFactoryBean { 26 | 27 | // Required 28 | public static final String GROUP = "group"; 29 | public static final String PORT = "port"; 30 | public static final String UDP_MODE = "udp-mode"; 31 | public static final String TTL = "ttl"; 32 | public static final String PERIOD = "period"; 33 | 34 | // Optional 35 | public static final String DURATION_UNIT = "duration-unit"; 36 | public static final String RATE_UNIT = "rate-unit"; 37 | public static final String PROTOCOL = "protocol"; 38 | public static final String UUID = "uuid"; 39 | public static final String SPOOF = "spoof"; 40 | public static final String DMAX = "dmax"; 41 | public static final String TMAX = "tmax"; 42 | 43 | @Override 44 | public Class getObjectType() { 45 | return GangliaReporter.class; 46 | } 47 | 48 | @SuppressWarnings("resource") 49 | @Override 50 | protected GangliaReporter createInstance() throws Exception { 51 | final GangliaReporter.Builder reporter = GangliaReporter.forRegistry(getMetricRegistry()); 52 | 53 | if (hasProperty(DURATION_UNIT)) { 54 | reporter.convertDurationsTo(getProperty(DURATION_UNIT, TimeUnit.class)); 55 | } 56 | 57 | if (hasProperty(RATE_UNIT)) { 58 | reporter.convertRatesTo(getProperty(RATE_UNIT, TimeUnit.class)); 59 | } 60 | 61 | reporter.filter(getMetricFilter()); 62 | reporter.prefixedWith(getPrefix()); 63 | 64 | if (hasProperty(DMAX)) { 65 | reporter.withDMax(getProperty(DMAX, Integer.TYPE)); 66 | } 67 | 68 | if (hasProperty(TMAX)) { 69 | reporter.withTMax(getProperty(TMAX, Integer.TYPE)); 70 | } 71 | 72 | final GMetric gMetric = new GMetric(getProperty(GROUP), getProperty(PORT, Integer.TYPE), getProperty(UDP_MODE, UDPAddressingMode.class), getProperty( 73 | TTL, Integer.TYPE), !hasProperty(PROTOCOL) || getProperty(PROTOCOL).contains("3.1"), 74 | hasProperty(UUID) ? java.util.UUID.fromString(getProperty(UUID)) : null, getProperty(SPOOF)); 75 | 76 | return reporter.build(gMetric); 77 | } 78 | 79 | @Override 80 | protected long getPeriod() { 81 | return convertDurationString(getProperty(PERIOD)); 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/spring/reporter/GraphiteReporterElementParser.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring.reporter; 17 | 18 | import static com.ryantenney.metrics.spring.reporter.GraphiteReporterFactoryBean.*; 19 | 20 | public class GraphiteReporterElementParser extends AbstractReporterElementParser { 21 | 22 | @Override 23 | public String getType() { 24 | return "graphite"; 25 | } 26 | 27 | @Override 28 | protected Class getBeanClass() { 29 | return GraphiteReporterFactoryBean.class; 30 | } 31 | 32 | @Override 33 | protected void validate(ValidationContext c) { 34 | c.require(PERIOD, DURATION_STRING_REGEX, "Period is required and must be in the form '\\d+(ns|us|ms|s|m|h|d)'"); 35 | 36 | c.optional(TRANSPORT); 37 | 38 | // TCP, UDP, Pickle 39 | if (!c.has(TRANSPORT) || c.get(TRANSPORT).matches("^tcp|udp|pickle$")) { 40 | c.require(HOST); 41 | c.require(PORT, PORT_NUMBER_REGEX, "Port number is required and must be between 1-65536"); 42 | 43 | c.optional(CHARSET); 44 | 45 | if ("pickle".equals(c.get(TRANSPORT))) { 46 | c.optional(BATCH_SIZE); 47 | } 48 | } 49 | else if (c.get(TRANSPORT).equals("rabbitmq")) { 50 | c.require(CONNECTION_FACTORY_REF); 51 | c.require(EXCHANGE); 52 | } 53 | 54 | c.optional(CLOCK_REF); 55 | 56 | c.optional(RATE_UNIT, TIMEUNIT_STRING_REGEX, "Rate unit must be one of the enum constants from java.util.concurrent.TimeUnit"); 57 | c.optional(DURATION_UNIT, TIMEUNIT_STRING_REGEX, "Duration unit must be one of the enum constants from java.util.concurrent.TimeUnit"); 58 | 59 | c.optional(PREFIX); 60 | c.optional(PREFIX_SUPPLIER_REF); 61 | if (c.has(PREFIX) && c.has(PREFIX_SUPPLIER_REF)) { 62 | c.reject(PREFIX_SUPPLIER_REF, "Reporter element must not specify both the 'prefix' and 'prefix-supplier-ref' attributes"); 63 | } 64 | 65 | c.optional(FILTER_PATTERN); 66 | c.optional(FILTER_REF); 67 | if (c.has(FILTER_PATTERN) && c.has(FILTER_REF)) { 68 | c.reject(FILTER_REF, "Reporter element must not specify both the 'filter' and 'filter-ref' attributes"); 69 | } 70 | 71 | c.rejectUnmatchedProperties(); 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/spring/reporter/GraphiteReporterFactoryBean.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring.reporter; 17 | 18 | import java.nio.charset.Charset; 19 | import java.util.concurrent.TimeUnit; 20 | 21 | import javax.net.SocketFactory; 22 | 23 | import com.codahale.metrics.Clock; 24 | import com.codahale.metrics.graphite.Graphite; 25 | import com.codahale.metrics.graphite.GraphiteRabbitMQ; 26 | import com.codahale.metrics.graphite.GraphiteReporter; 27 | import com.codahale.metrics.graphite.GraphiteSender; 28 | import com.codahale.metrics.graphite.GraphiteUDP; 29 | import com.codahale.metrics.graphite.PickledGraphite; 30 | import com.rabbitmq.client.ConnectionFactory; 31 | 32 | public class GraphiteReporterFactoryBean extends AbstractScheduledReporterFactoryBean { 33 | 34 | // Required 35 | public static final String HOST = "host"; 36 | public static final String PORT = "port"; 37 | public static final String PERIOD = "period"; 38 | 39 | // Optional 40 | public static final String TRANSPORT = "transport"; 41 | public static final String CHARSET = "charset"; 42 | public static final String CLOCK_REF = "clock-ref"; 43 | public static final String DURATION_UNIT = "duration-unit"; 44 | public static final String RATE_UNIT = "rate-unit"; 45 | 46 | // Pickle Optional 47 | public static final String BATCH_SIZE = "batch-size"; 48 | 49 | // RabbitMQ Required 50 | public static final String CONNECTION_FACTORY_REF = "connection-factory-ref"; 51 | public static final String EXCHANGE = "exchange"; 52 | 53 | @Override 54 | public Class getObjectType() { 55 | return GraphiteReporter.class; 56 | } 57 | 58 | @SuppressWarnings("resource") 59 | @Override 60 | protected GraphiteReporter createInstance() { 61 | final GraphiteReporter.Builder reporter = GraphiteReporter.forRegistry(getMetricRegistry()); 62 | 63 | if (hasProperty(CLOCK_REF)) { 64 | reporter.withClock(getPropertyRef(CLOCK_REF, Clock.class)); 65 | } 66 | 67 | if (hasProperty(DURATION_UNIT)) { 68 | reporter.convertDurationsTo(getProperty(DURATION_UNIT, TimeUnit.class)); 69 | } 70 | 71 | if (hasProperty(RATE_UNIT)) { 72 | reporter.convertRatesTo(getProperty(RATE_UNIT, TimeUnit.class)); 73 | } 74 | 75 | reporter.filter(getMetricFilter()); 76 | reporter.prefixedWith(getPrefix()); 77 | 78 | final String transport = getProperty(TRANSPORT, "tcp"); 79 | final Charset charset = Charset.forName(getProperty(CHARSET, "UTF-8")); 80 | final GraphiteSender graphite; 81 | 82 | if ("rabbitmq".equals(transport)) { 83 | ConnectionFactory connectionFactory = getPropertyRef(CONNECTION_FACTORY_REF, ConnectionFactory.class); 84 | String exchange = getProperty(EXCHANGE); 85 | graphite = new GraphiteRabbitMQ(connectionFactory, exchange); 86 | } 87 | else { 88 | final String hostname = getProperty(HOST); 89 | final int port = getProperty(PORT, Integer.TYPE); 90 | 91 | if ("tcp".equals(transport)) { 92 | graphite = new Graphite(hostname, port, SocketFactory.getDefault(), charset); 93 | } 94 | else if ("udp".equals(transport)) { 95 | graphite = new GraphiteUDP(hostname, port); 96 | } 97 | else if ("pickle".equals(transport)) { 98 | graphite = new PickledGraphite(hostname, port, SocketFactory.getDefault(), charset, getProperty(BATCH_SIZE, Integer.TYPE, 100)); 99 | } 100 | else { 101 | throw new IllegalArgumentException("Invalid graphite transport: " + transport); 102 | } 103 | } 104 | 105 | return reporter.build(graphite); 106 | } 107 | 108 | @Override 109 | protected long getPeriod() { 110 | return convertDurationString(getProperty(PERIOD)); 111 | } 112 | 113 | } 114 | -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/spring/reporter/JmxReporterElementParser.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring.reporter; 17 | 18 | import static com.ryantenney.metrics.spring.reporter.JmxReporterFactoryBean.*; 19 | 20 | public class JmxReporterElementParser extends AbstractReporterElementParser { 21 | 22 | @Override 23 | public String getType() { 24 | return "jmx"; 25 | } 26 | 27 | @Override 28 | protected Class getBeanClass() { 29 | return JmxReporterFactoryBean.class; 30 | } 31 | 32 | @Override 33 | protected void validate(ValidationContext c) { 34 | c.optional(DOMAIN); 35 | c.optional(MBEAN_SERVER_REF); 36 | 37 | c.optional(RATE_UNIT, TIMEUNIT_STRING_REGEX, "Rate unit must be one of the enum constants from java.util.concurrent.TimeUnit"); 38 | c.optional(DURATION_UNIT, TIMEUNIT_STRING_REGEX, "Duration unit must be one of the enum constants from java.util.concurrent.TimeUnit"); 39 | 40 | c.optional(FILTER_PATTERN); 41 | c.optional(FILTER_REF); 42 | if (c.has(FILTER_PATTERN) && c.has(FILTER_REF)) { 43 | c.reject(FILTER_REF, "Reporter element must not specify both the 'filter' and 'filter-ref' attributes"); 44 | } 45 | 46 | c.rejectUnmatchedProperties(); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/spring/reporter/JmxReporterFactoryBean.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring.reporter; 17 | 18 | import com.codahale.metrics.JmxReporter; 19 | import org.springframework.beans.factory.DisposableBean; 20 | import org.springframework.context.SmartLifecycle; 21 | 22 | import javax.management.MBeanServer; 23 | import java.util.concurrent.TimeUnit; 24 | 25 | public class JmxReporterFactoryBean extends AbstractReporterFactoryBean implements SmartLifecycle, DisposableBean { 26 | 27 | // Optional 28 | public static final String DOMAIN = "domain"; 29 | public static final String DURATION_UNIT = "duration-unit"; 30 | public static final String RATE_UNIT = "rate-unit"; 31 | public static final String MBEAN_SERVER_REF = "mbean-server-ref"; 32 | 33 | private boolean running = false; 34 | 35 | @Override 36 | public Class getObjectType() { 37 | return JmxReporter.class; 38 | } 39 | 40 | @Override 41 | protected JmxReporter createInstance() { 42 | final JmxReporter.Builder reporter = JmxReporter.forRegistry(getMetricRegistry()); 43 | 44 | if (hasProperty(DOMAIN)) { 45 | reporter.inDomain(getProperty(DOMAIN)); 46 | } 47 | 48 | if (hasProperty(DURATION_UNIT)) { 49 | reporter.convertDurationsTo(getProperty(DURATION_UNIT, TimeUnit.class)); 50 | } 51 | 52 | if (hasProperty(RATE_UNIT)) { 53 | reporter.convertRatesTo(getProperty(RATE_UNIT, TimeUnit.class)); 54 | } 55 | 56 | reporter.filter(getMetricFilter()); 57 | 58 | if (hasProperty(MBEAN_SERVER_REF)) { 59 | reporter.registerWith(getPropertyRef(MBEAN_SERVER_REF, MBeanServer.class)); 60 | } 61 | 62 | return reporter.build(); 63 | } 64 | 65 | @Override 66 | public void start() { 67 | if (isEnabled() && !isRunning()) { 68 | getObject().start(); 69 | running = true; 70 | } 71 | } 72 | 73 | @Override 74 | public void stop() { 75 | if (isRunning()) { 76 | getObject().stop(); 77 | running = false; 78 | } 79 | } 80 | 81 | @Override 82 | public boolean isRunning() { 83 | return running; 84 | } 85 | 86 | @Override 87 | public void destroy() throws Exception { 88 | stop(); 89 | } 90 | 91 | @Override 92 | public boolean isAutoStartup() { 93 | return true; 94 | } 95 | 96 | @Override 97 | public void stop(Runnable runnable) { 98 | stop(); 99 | runnable.run(); 100 | } 101 | 102 | @Override 103 | public int getPhase() { 104 | return 0; 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/spring/reporter/LibratoReporterElementParser.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring.reporter; 17 | 18 | import static com.ryantenney.metrics.spring.reporter.LibratoReporterFactoryBean.*; 19 | 20 | public class LibratoReporterElementParser extends AbstractReporterElementParser { 21 | 22 | @Override 23 | public String getType() { 24 | return "librato"; 25 | } 26 | 27 | @Override 28 | protected Class getBeanClass() { 29 | return LibratoReporterFactoryBean.class; 30 | } 31 | 32 | @Override 33 | protected void validate(ValidationContext c) { 34 | c.require(USERNAME); 35 | c.require(TOKEN); 36 | c.require(PERIOD, DURATION_STRING_REGEX, "Period is required and must be in the form '\\d+(ns|us|ms|s|m|h|d)'"); 37 | 38 | c.optional(SOURCE); 39 | c.optional(SOURCE_SUPPLIER_REF); 40 | if (!c.has(SOURCE) && !c.has(SOURCE_SUPPLIER_REF)) { 41 | c.require(SOURCE); 42 | } 43 | else if (c.has(SOURCE) && c.has(SOURCE_SUPPLIER_REF)) { 44 | c.reject(SOURCE_SUPPLIER_REF, "Reporter element must not specify both the 'source' and 'source-supplier-ref' attributes"); 45 | } 46 | 47 | c.optional(TIMEOUT, DURATION_STRING_REGEX, "Timeout must be in the form '\\d+(ns|us|ms|s|m|h|d)'"); 48 | c.optional(NAME); 49 | c.optional(SANITIZER_REF); 50 | c.optional(PREFIX_DELIMITER); 51 | c.optional(CLOCK_REF); 52 | c.optional(SOURCE_REGEX); 53 | 54 | c.optional(HTTP_POSTER_REF); 55 | c.optional(HTTP_CLIENT_CONFIG_REF); 56 | 57 | c.optional(DELETE_IDLE_STATS); 58 | c.optional(OMIT_COMPLEX_GAUGES); 59 | 60 | c.optional(EXPANSION_CONFIG); 61 | c.optional(EXPANSION_CONFIG_REF); 62 | if (c.has(EXPANSION_CONFIG) && c.has(EXPANSION_CONFIG_REF)) { 63 | c.reject(EXPANSION_CONFIG, "Librato Reporter element must not specify both the 'expansion-config' and 'expansion-config-ref' attributes"); 64 | } 65 | 66 | c.optional(RATE_UNIT, TIMEUNIT_STRING_REGEX, "Rate unit must be one of the enum constants from java.util.concurrent.TimeUnit"); 67 | c.optional(DURATION_UNIT, TIMEUNIT_STRING_REGEX, "Duration unit must be one of the enum constants from java.util.concurrent.TimeUnit"); 68 | 69 | c.optional(PREFIX); 70 | c.optional(PREFIX_SUPPLIER_REF); 71 | if (c.has(PREFIX) && c.has(PREFIX_SUPPLIER_REF)) { 72 | c.reject(PREFIX_SUPPLIER_REF, "Reporter element must not specify both the 'prefix' and 'prefix-supplier-ref' attributes"); 73 | } 74 | 75 | c.optional(FILTER_PATTERN); 76 | c.optional(FILTER_REF); 77 | if (c.has(FILTER_PATTERN) && c.has(FILTER_REF)) { 78 | c.reject(FILTER_REF, "Reporter element must not specify both the 'filter' and 'filter-ref' attributes"); 79 | } 80 | 81 | c.rejectUnmatchedProperties(); 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/spring/reporter/MetricPrefixSupplier.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring.reporter; 17 | 18 | public interface MetricPrefixSupplier { 19 | 20 | String getPrefix(); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/spring/reporter/NewRelicReporterElementParser.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring.reporter; 17 | 18 | 19 | public class NewRelicReporterElementParser extends AbstractReporterElementParser { 20 | 21 | @Override 22 | public String getType() { 23 | return "newrelic"; 24 | } 25 | 26 | @Override 27 | protected void validate(ValidationContext context) { 28 | context.require(NewRelicReporterFactoryBean.PERIOD, DURATION_STRING_REGEX, "Period is required and must be in the form '\\d+(ns|us|ms|s|m|h|d)'"); 29 | 30 | context.optional(NewRelicReporterFactoryBean.NAME); 31 | context.optional(NewRelicReporterFactoryBean.RATE_UNIT, TIMEUNIT_STRING_REGEX, "Rate unit must be one of the enum constants from java.util.concurrent.TimeUnit"); 32 | context.optional(NewRelicReporterFactoryBean.DURATION_UNIT, TIMEUNIT_STRING_REGEX, "Duration unit must be one of the enum constants from java.util.concurrent.TimeUnit"); 33 | context.optional(NewRelicReporterFactoryBean.ATTRIBUTE_FILTER); 34 | context.optional(NewRelicReporterFactoryBean.PREFIX); 35 | 36 | context.rejectUnmatchedProperties(); 37 | } 38 | 39 | protected Class getBeanClass() { 40 | return NewRelicReporterFactoryBean.class; 41 | } 42 | 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/spring/reporter/NewRelicReporterFactoryBean.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring.reporter; 17 | 18 | import com.codahale.metrics.MetricFilter; 19 | import com.palominolabs.metrics.newrelic.AllEnabledMetricAttributeFilter; 20 | import com.palominolabs.metrics.newrelic.MetricAttributeFilter; 21 | import com.palominolabs.metrics.newrelic.NewRelicReporter; 22 | import org.slf4j.Logger; 23 | import org.slf4j.LoggerFactory; 24 | 25 | import java.util.concurrent.TimeUnit; 26 | 27 | public class NewRelicReporterFactoryBean extends AbstractScheduledReporterFactoryBean { 28 | 29 | private static final Logger LOG = LoggerFactory.getLogger(NewRelicReporterFactoryBean.class); 30 | private static final String EMPTY_STRING = ""; 31 | 32 | protected static final String PREFIX = "prefix"; 33 | protected static final String PERIOD = "period"; 34 | protected static final String DURATION_UNIT = "duration-unit"; 35 | protected static final String RATE_UNIT = "rate-unit"; 36 | protected static final String NAME = "name"; 37 | protected static final String ATTRIBUTE_FILTER = "attribute-filter-ref"; 38 | 39 | @Override 40 | protected long getPeriod() { 41 | return convertDurationString(getProperty(PERIOD)); 42 | } 43 | 44 | @Override 45 | public Class getObjectType() { 46 | return NewRelicReporter.class; 47 | } 48 | 49 | @Override 50 | protected NewRelicReporter createInstance() throws Exception { 51 | 52 | String prefix = this.getProperty(PREFIX, String.class, EMPTY_STRING); 53 | MetricFilter metricFilter = getMetricFilter(); 54 | TimeUnit duration = this.getProperty(DURATION_UNIT, TimeUnit.class, TimeUnit.MILLISECONDS); 55 | TimeUnit rateUnit = this.getProperty(RATE_UNIT, TimeUnit.class, TimeUnit.SECONDS); 56 | String name = this.getProperty(NAME, String.class, "NewRelic reporter"); 57 | MetricAttributeFilter attributeFilter = this.hasProperty(ATTRIBUTE_FILTER) ? 58 | this.getPropertyRef(ATTRIBUTE_FILTER, MetricAttributeFilter.class) : 59 | new AllEnabledMetricAttributeFilter(); 60 | 61 | LOG.debug("Creating instance of NewRelicReporter with name '{}', prefix '{}', rate unit '{}', duration '{}', filter '{}' and attribute filter '{}'", 62 | name, prefix, rateUnit, duration, metricFilter, attributeFilter.getClass().getSimpleName()); 63 | 64 | return NewRelicReporter.forRegistry(this.getMetricRegistry()) 65 | .name(name) 66 | .filter(metricFilter) 67 | .attributeFilter(attributeFilter) 68 | .rateUnit(rateUnit) 69 | .durationUnit(duration) 70 | .metricNamePrefix(prefix) 71 | .build(); 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/spring/reporter/ReporterElementParser.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring.reporter; 17 | 18 | import org.springframework.beans.factory.support.AbstractBeanDefinition; 19 | import org.springframework.beans.factory.xml.ParserContext; 20 | import org.w3c.dom.Element; 21 | 22 | public interface ReporterElementParser { 23 | 24 | String getType(); 25 | 26 | AbstractBeanDefinition parseReporter(Element element, ParserContext parserContext); 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/spring/reporter/Slf4jReporterElementParser.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring.reporter; 17 | 18 | import static com.ryantenney.metrics.spring.reporter.Slf4jReporterFactoryBean.*; 19 | 20 | public class Slf4jReporterElementParser extends AbstractReporterElementParser { 21 | 22 | protected static final String LOG_LEVEL_STRING_REGEX = "^(?:TRACE|DEBUG|INFO|WARN|ERROR)$"; 23 | 24 | @Override 25 | public String getType() { 26 | return "slf4j"; 27 | } 28 | 29 | @Override 30 | protected Class getBeanClass() { 31 | return Slf4jReporterFactoryBean.class; 32 | } 33 | 34 | @Override 35 | protected void validate(ValidationContext c) { 36 | c.require(PERIOD, DURATION_STRING_REGEX, "Period is required and must be in the form '\\d+(ns|us|ms|s|m|h|d)'"); 37 | c.optional(MARKER); 38 | c.optional(LOGGER); 39 | c.optional(LEVEL, LOG_LEVEL_STRING_REGEX, "Level must be one of the enum constants from com.codahale.metrics.Slf4jReporter.LoggingLevel"); 40 | 41 | c.optional(RATE_UNIT, TIMEUNIT_STRING_REGEX, "Rate unit must be one of the enum constants from java.util.concurrent.TimeUnit"); 42 | c.optional(DURATION_UNIT, TIMEUNIT_STRING_REGEX, "Duration unit must be one of the enum constants from java.util.concurrent.TimeUnit"); 43 | 44 | c.optional(PREFIX); 45 | c.optional(PREFIX_SUPPLIER_REF); 46 | if (c.has(PREFIX) && c.has(PREFIX_SUPPLIER_REF)) { 47 | c.reject(PREFIX_SUPPLIER_REF, "Reporter element must not specify both the 'prefix' and 'prefix-supplier-ref' attributes"); 48 | } 49 | 50 | c.optional(FILTER_PATTERN); 51 | c.optional(FILTER_REF); 52 | if (c.has(FILTER_PATTERN) && c.has(FILTER_REF)) { 53 | c.reject(FILTER_REF, "Reporter element must not specify both the 'filter' and 'filter-ref' attributes"); 54 | } 55 | 56 | c.rejectUnmatchedProperties(); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/spring/reporter/Slf4jReporterFactoryBean.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring.reporter; 17 | 18 | import java.util.concurrent.TimeUnit; 19 | 20 | import org.slf4j.LoggerFactory; 21 | import org.slf4j.MarkerFactory; 22 | 23 | import com.codahale.metrics.Slf4jReporter; 24 | import com.codahale.metrics.Slf4jReporter.LoggingLevel; 25 | 26 | public class Slf4jReporterFactoryBean extends AbstractScheduledReporterFactoryBean { 27 | 28 | // Required 29 | public static final String PERIOD = "period"; 30 | 31 | // Optional 32 | public static final String DURATION_UNIT = "duration-unit"; 33 | public static final String RATE_UNIT = "rate-unit"; 34 | public static final String MARKER = "marker"; 35 | public static final String LOGGER = "logger"; 36 | public static final String LEVEL = "level"; 37 | 38 | @Override 39 | public Class getObjectType() { 40 | return Slf4jReporter.class; 41 | } 42 | 43 | @Override 44 | protected Slf4jReporter createInstance() { 45 | final Slf4jReporter.Builder reporter = Slf4jReporter.forRegistry(getMetricRegistry()); 46 | 47 | if (hasProperty(DURATION_UNIT)) { 48 | reporter.convertDurationsTo(getProperty(DURATION_UNIT, TimeUnit.class)); 49 | } 50 | 51 | if (hasProperty(RATE_UNIT)) { 52 | reporter.convertRatesTo(getProperty(RATE_UNIT, TimeUnit.class)); 53 | } 54 | 55 | reporter.filter(getMetricFilter()); 56 | reporter.prefixedWith(getPrefix()); 57 | 58 | if (hasProperty(MARKER)) { 59 | reporter.markWith(MarkerFactory.getMarker(getProperty(MARKER))); 60 | } 61 | 62 | if (hasProperty(LOGGER)) { 63 | reporter.outputTo(LoggerFactory.getLogger(getProperty(LOGGER))); 64 | } 65 | 66 | if (hasProperty(LEVEL)) { 67 | reporter.withLoggingLevel(getProperty(LEVEL, LoggingLevel.class)); 68 | } 69 | 70 | return reporter.build(); 71 | } 72 | 73 | @Override 74 | protected long getPeriod() { 75 | return convertDurationString(getProperty(PERIOD)); 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/com/ryantenney/metrics/spring/servlets/MetricsServletsContextListener.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring.servlets; 17 | 18 | import javax.servlet.ServletContextEvent; 19 | import javax.servlet.ServletContextListener; 20 | 21 | import org.springframework.beans.factory.annotation.Autowired; 22 | import org.springframework.web.context.support.WebApplicationContextUtils; 23 | 24 | import com.codahale.metrics.MetricRegistry; 25 | import com.codahale.metrics.health.HealthCheckRegistry; 26 | import com.codahale.metrics.servlets.MetricsServlet; 27 | import com.codahale.metrics.servlets.HealthCheckServlet; 28 | 29 | public class MetricsServletsContextListener implements ServletContextListener { 30 | 31 | @Autowired 32 | private MetricRegistry metricRegistry; 33 | 34 | @Autowired 35 | private HealthCheckRegistry healthCheckRegistry; 36 | 37 | private final MetricsServletContextListener metricsServletContextListener = new MetricsServletContextListener(); 38 | private final HealthCheckServletContextListener healthCheckServletContextListener = new HealthCheckServletContextListener(); 39 | 40 | @Override 41 | public void contextInitialized(ServletContextEvent event) { 42 | WebApplicationContextUtils.getRequiredWebApplicationContext(event.getServletContext()).getAutowireCapableBeanFactory().autowireBean(this); 43 | 44 | metricsServletContextListener.contextInitialized(event); 45 | healthCheckServletContextListener.contextInitialized(event); 46 | } 47 | 48 | @Override 49 | public void contextDestroyed(ServletContextEvent event) {} 50 | 51 | class MetricsServletContextListener extends MetricsServlet.ContextListener { 52 | 53 | @Override 54 | protected MetricRegistry getMetricRegistry() { 55 | return metricRegistry; 56 | } 57 | 58 | } 59 | 60 | class HealthCheckServletContextListener extends HealthCheckServlet.ContextListener { 61 | 62 | @Override 63 | protected HealthCheckRegistry getHealthCheckRegistry() { 64 | return healthCheckRegistry; 65 | } 66 | 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/services/com.ryantenney.metrics.spring.reporter.ReporterElementParser: -------------------------------------------------------------------------------- 1 | com.ryantenney.metrics.spring.reporter.JmxReporterElementParser 2 | com.ryantenney.metrics.spring.reporter.Slf4jReporterElementParser 3 | com.ryantenney.metrics.spring.reporter.ConsoleReporterElementParser 4 | com.ryantenney.metrics.spring.reporter.CsvReporterElementParser 5 | com.ryantenney.metrics.spring.reporter.GraphiteReporterElementParser 6 | com.ryantenney.metrics.spring.reporter.GangliaReporterElementParser 7 | com.ryantenney.metrics.spring.reporter.LibratoReporterElementParser 8 | com.ryantenney.metrics.spring.reporter.NewRelicReporterElementParser 9 | com.ryantenney.metrics.spring.reporter.DatadogReporterElementParser 10 | com.ryantenney.metrics.spring.reporter.ElasticSearchReporterElementParser 11 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/spring.handlers: -------------------------------------------------------------------------------- 1 | http\://www.ryantenney.com/schema/metrics=com.ryantenney.metrics.spring.config.MetricsNamespaceHandler -------------------------------------------------------------------------------- /src/main/resources/META-INF/spring.schemas: -------------------------------------------------------------------------------- 1 | http\://www.ryantenney.com/schema/metrics/metrics.xsd=com/ryantenney/metrics/spring/config/metrics.xsd 2 | http\://www.ryantenney.com/schema/metrics/metrics-3.0.xsd=com/ryantenney/metrics/spring/config/metrics-3.0.xsd 3 | http\://www.ryantenney.com/schema/metrics/metrics-3.1.xsd=com/ryantenney/metrics/spring/config/metrics-3.1.xsd 4 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/spring.tooling: -------------------------------------------------------------------------------- 1 | # Tooling related information for the metrics namespace 2 | http\://www.ryantenney.com/schema/metrics@name=metrics Namespace 3 | http\://www.ryantenney.com/schema/metrics@prefix=metrics 4 | http\://www.ryantenney.com/schema/metrics@icon=com/ryantenney/metrics/spring/config/metrics.gif -------------------------------------------------------------------------------- /src/main/resources/com/ryantenney/metrics/spring/config/metrics.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryantenney/metrics-spring/1c0803e0bee3925cdee84a8dfb5d6ff5c415b99d/src/main/resources/com/ryantenney/metrics/spring/config/metrics.gif -------------------------------------------------------------------------------- /src/test/java/com/ryantenney/metrics/spring/AopFieldInjectionInteractionTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring; 17 | 18 | import static org.hamcrest.Matchers.*; 19 | import static org.junit.Assert.assertThat; 20 | 21 | import org.aspectj.lang.ProceedingJoinPoint; 22 | import org.aspectj.lang.annotation.Around; 23 | import org.aspectj.lang.annotation.Aspect; 24 | import org.junit.Test; 25 | import org.junit.runner.RunWith; 26 | import org.springframework.beans.factory.annotation.Autowired; 27 | import org.springframework.test.context.ContextConfiguration; 28 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 29 | 30 | import com.codahale.metrics.Counter; 31 | import com.codahale.metrics.MetricRegistry; 32 | import com.codahale.metrics.annotation.Metric; 33 | 34 | @RunWith(SpringJUnit4ClassRunner.class) 35 | @ContextConfiguration("classpath:aop-field-injection-interaction.xml") 36 | public class AopFieldInjectionInteractionTest { 37 | 38 | @Autowired 39 | private MetricRegistry metricRegistry; 40 | @Autowired 41 | private TestAspectTarget target; 42 | 43 | @Test 44 | public void testFieldInjectionShouldNotCauseErrorWhenTargetIsAopProxy() throws Exception { 45 | // verify that AOP interception is working 46 | assertThat(target.targetMethod(), is(5)); 47 | 48 | assertThat(metricRegistry.getCounters(), hasKey("targetCounter")); 49 | assertThat(metricRegistry.getCounters().get("targetCounter").getCount(), is(1L)); 50 | } 51 | 52 | } 53 | 54 | @Aspect 55 | class TestAspectWrapper { 56 | 57 | @Around("execution(* com.ryantenney.metrics.spring.TestAspectTarget.targetMethod())") 58 | public Object aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable { 59 | joinPoint.proceed(); 60 | return 5; 61 | } 62 | 63 | } 64 | 65 | interface TestAspectTarget { 66 | 67 | public int targetMethod(); 68 | 69 | } 70 | 71 | class TestAspectTargetImpl implements TestAspectTarget { 72 | 73 | @Metric(name = "targetCounter", absolute = true) 74 | private Counter counter; 75 | 76 | @Override 77 | public int targetMethod() { 78 | this.counter.inc(); 79 | return 3; 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /src/test/java/com/ryantenney/metrics/spring/CovariantReturnTypeTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring; 17 | 18 | import org.junit.After; 19 | import org.junit.Assert; 20 | import org.junit.Before; 21 | import org.junit.Test; 22 | import org.springframework.beans.factory.NoSuchBeanDefinitionException; 23 | import org.springframework.context.support.ClassPathXmlApplicationContext; 24 | 25 | import com.codahale.metrics.MetricRegistry; 26 | import com.codahale.metrics.annotation.Counted; 27 | import com.codahale.metrics.annotation.ExceptionMetered; 28 | import com.codahale.metrics.annotation.Metered; 29 | import com.codahale.metrics.annotation.Timed; 30 | 31 | public class CovariantReturnTypeTest { 32 | 33 | ClassPathXmlApplicationContext ctx; 34 | MetricRegistry metricRegistry; 35 | 36 | @Before 37 | public void init() { 38 | this.ctx = new ClassPathXmlApplicationContext("classpath:covariant-return-type.xml"); 39 | this.metricRegistry = this.ctx.getBean(MetricRegistry.class); 40 | } 41 | 42 | @After 43 | public void destroy() throws Throwable { 44 | this.ctx.stop(); 45 | } 46 | 47 | @Test 48 | public void noMetricsRegistered() { 49 | Assert.assertTrue("No metrics registered", this.metricRegistry.getNames().isEmpty()); 50 | } 51 | 52 | @Test 53 | public void testMeteredInterface() { 54 | MeteredInterface mi = ctx.getBean(MeteredInterface.class); 55 | Assert.assertNotNull("Expected to be able to get MeteredInterface by interface and not by class.", mi); 56 | } 57 | 58 | @Test(expected = NoSuchBeanDefinitionException.class) 59 | public void testMeteredInterfaceImpl() { 60 | MeteredInterfaceImpl mc = ctx.getBean(MeteredInterfaceImpl.class); 61 | Assert.assertNull("Expected to be unable to get MeteredInterfaceImpl by class.", mc); 62 | } 63 | 64 | @Test 65 | public void testTimedMethod() { 66 | ctx.getBean(MeteredInterface.class).timedMethod(); 67 | Assert.assertTrue("No metrics should be registered", this.metricRegistry.getNames().isEmpty()); 68 | } 69 | 70 | @Test 71 | public void testMeteredMethod() { 72 | ctx.getBean(MeteredInterface.class).meteredMethod(); 73 | Assert.assertTrue("No metrics should be registered", this.metricRegistry.getNames().isEmpty()); 74 | } 75 | 76 | @Test(expected = BogusException.class) 77 | public void testExceptionMeteredMethod() throws Throwable { 78 | try { 79 | ctx.getBean(MeteredInterface.class).exceptionMeteredMethod(); 80 | } 81 | catch (Throwable t) { 82 | Assert.assertTrue("No metrics should be registered", this.metricRegistry.getNames().isEmpty()); 83 | throw t; 84 | } 85 | } 86 | 87 | @Test 88 | public void testCountedMethod() { 89 | ctx.getBean(MeteredInterface.class).countedMethod(); 90 | Assert.assertTrue("No metrics should be registered", this.metricRegistry.getNames().isEmpty()); 91 | } 92 | 93 | public interface MeteredInterface { 94 | 95 | @Timed 96 | public Number timedMethod(); 97 | 98 | @Metered 99 | public Number meteredMethod(); 100 | 101 | @ExceptionMetered 102 | public Number exceptionMeteredMethod() throws Throwable; 103 | 104 | @Counted 105 | public Number countedMethod(); 106 | 107 | } 108 | 109 | public static class MeteredInterfaceImpl implements MeteredInterface { 110 | 111 | @Override 112 | public Integer timedMethod() { 113 | return 0; 114 | } 115 | 116 | @Override 117 | public Long meteredMethod() { 118 | return 0L; 119 | } 120 | 121 | @Override 122 | public Byte exceptionMeteredMethod() throws Throwable { 123 | throw new BogusException(); 124 | } 125 | 126 | @Override 127 | public Double countedMethod() { 128 | return 0.0; 129 | } 130 | 131 | } 132 | 133 | @SuppressWarnings("serial") 134 | public static class BogusException extends Throwable {} 135 | 136 | } 137 | -------------------------------------------------------------------------------- /src/test/java/com/ryantenney/metrics/spring/HealthCheckTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring; 17 | 18 | import static org.junit.Assert.assertEquals; 19 | import static org.junit.Assert.assertFalse; 20 | import static org.junit.Assert.assertTrue; 21 | 22 | import org.junit.Test; 23 | import org.junit.runner.RunWith; 24 | import org.springframework.beans.factory.annotation.Autowired; 25 | import org.springframework.test.context.ContextConfiguration; 26 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 27 | 28 | import com.codahale.metrics.health.HealthCheck; 29 | import com.codahale.metrics.health.HealthCheck.Result; 30 | import com.codahale.metrics.health.HealthCheckRegistry; 31 | 32 | @RunWith(SpringJUnit4ClassRunner.class) 33 | @ContextConfiguration("classpath:health-check.xml") 34 | public class HealthCheckTest { 35 | 36 | @Autowired 37 | HealthCheckClass healthCheckClass; 38 | 39 | @Autowired 40 | HealthCheckRegistry healthCheckRegistry; 41 | 42 | @Test 43 | public void testHealthy() { 44 | healthCheckClass.setShouldFail(false); 45 | Result result = healthCheckRegistry.runHealthCheck("myHealthCheckClass"); 46 | assertTrue(result.isHealthy()); 47 | } 48 | 49 | @Test 50 | public void testUnhealthy() { 51 | healthCheckClass.setShouldFail(true); 52 | Result result = healthCheckRegistry.runHealthCheck("myHealthCheckClass"); 53 | assertFalse(result.isHealthy()); 54 | assertEquals("fail whale", result.getMessage()); 55 | } 56 | 57 | public static class HealthCheckClass extends HealthCheck { 58 | 59 | private boolean shouldFail; 60 | 61 | public boolean isShouldFail() { 62 | return shouldFail; 63 | } 64 | 65 | public void setShouldFail(boolean shouldFail) { 66 | this.shouldFail = shouldFail; 67 | } 68 | 69 | @Override 70 | protected Result check() { 71 | if (shouldFail) { 72 | return Result.unhealthy(new RuntimeException("fail whale")); 73 | } 74 | return Result.healthy(); 75 | } 76 | 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/test/java/com/ryantenney/metrics/spring/LegacyMetricAnnotationTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring; 17 | 18 | import static com.ryantenney.metrics.spring.TestUtil.forLegacyMetricField; 19 | import static org.junit.Assert.assertNotNull; 20 | import static org.junit.Assert.assertSame; 21 | 22 | import org.junit.Test; 23 | import org.junit.runner.RunWith; 24 | import org.springframework.beans.factory.annotation.Autowired; 25 | import org.springframework.beans.factory.annotation.Qualifier; 26 | import org.springframework.test.context.ContextConfiguration; 27 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 28 | 29 | import com.codahale.metrics.Counter; 30 | import com.codahale.metrics.Histogram; 31 | import com.codahale.metrics.Meter; 32 | import com.codahale.metrics.MetricRegistry; 33 | import com.codahale.metrics.Timer; 34 | import com.codahale.metrics.UniformReservoir; 35 | import com.ryantenney.metrics.annotation.Metric; 36 | 37 | @SuppressWarnings("deprecation") 38 | @RunWith(SpringJUnit4ClassRunner.class) 39 | @ContextConfiguration("classpath:legacy-metric-annotation.xml") 40 | public class LegacyMetricAnnotationTest { 41 | 42 | @Autowired 43 | @Qualifier("target1") 44 | LegacyMetricAnnotationTest.Target target; 45 | 46 | @Autowired 47 | @Qualifier("target2") 48 | LegacyMetricAnnotationTest.Target target2; 49 | 50 | @Autowired 51 | MetricRegistry metricRegistry; 52 | 53 | @Test 54 | public void targetIsNotNull() throws Exception { 55 | assertNotNull(target); 56 | assertNotNull(target2); 57 | 58 | assertNotNull(target.theNameForTheMeter); 59 | assertNotNull(target2.theNameForTheMeter); 60 | Meter meter = (Meter) forLegacyMetricField(metricRegistry, LegacyMetricAnnotationTest.Target.class, "theNameForTheMeter"); 61 | assertSame(target.theNameForTheMeter, meter); 62 | assertSame(target2.theNameForTheMeter, meter); 63 | 64 | assertNotNull(target.timer); 65 | assertNotNull(target2.timer); 66 | Timer timer = (Timer) forLegacyMetricField(metricRegistry, LegacyMetricAnnotationTest.Target.class, "timer"); 67 | assertSame(target.timer, timer); 68 | assertSame(target2.timer, timer); 69 | 70 | assertNotNull(target.counter); 71 | assertNotNull(target2.counter); 72 | Counter ctr = (Counter) forLegacyMetricField(metricRegistry, LegacyMetricAnnotationTest.Target.class, "counter"); 73 | assertSame(target.counter, ctr); 74 | assertSame(target2.counter, ctr); 75 | 76 | assertNotNull(target.histogram); 77 | assertNotNull(target2.histogram); 78 | Histogram hist = (Histogram) forLegacyMetricField(metricRegistry, LegacyMetricAnnotationTest.Target.class, "histogram"); 79 | assertSame(target.histogram, hist); 80 | assertSame(target2.histogram, hist); 81 | 82 | assertNotNull(target.uniformHistogram); 83 | assertNotNull(target2.uniformHistogram); 84 | Histogram uniHist = (Histogram) forLegacyMetricField(metricRegistry, LegacyMetricAnnotationTest.Target.class, "uniformHistogram"); 85 | assertSame(target.uniformHistogram, uniHist); 86 | assertSame(target2.uniformHistogram, uniHist); 87 | } 88 | 89 | public static class Target { 90 | 91 | @Metric 92 | public Meter theNameForTheMeter; 93 | 94 | @Metric(name = "theNameForTheTimer") 95 | private Timer timer; 96 | 97 | @Metric(name = "group.type.counter", absolute = true) 98 | Counter counter; 99 | 100 | @Metric 101 | Histogram histogram; 102 | 103 | @Metric 104 | Histogram uniformHistogram = new Histogram(new UniformReservoir()); 105 | 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /src/test/java/com/ryantenney/metrics/spring/LoggingMetricRegistryListener.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring; 17 | 18 | import org.slf4j.Logger; 19 | import org.slf4j.LoggerFactory; 20 | 21 | import com.codahale.metrics.Counter; 22 | import com.codahale.metrics.Gauge; 23 | import com.codahale.metrics.Histogram; 24 | import com.codahale.metrics.Meter; 25 | import com.codahale.metrics.MetricRegistryListener; 26 | import com.codahale.metrics.Timer; 27 | 28 | /** 29 | * Created with IntelliJ IDEA. 30 | * User: ryan 31 | * Date: 4/2/13 32 | * Time: 5:50 PM 33 | * To change this template use File | Settings | File Templates. 34 | */ 35 | class LoggingMetricRegistryListener implements MetricRegistryListener { 36 | 37 | private static final Logger log = LoggerFactory.getLogger(LoggingMetricRegistryListener.class); 38 | 39 | @Override 40 | public void onGaugeAdded(String s, Gauge gauge) { 41 | log.info("Gauge added: {}", s); 42 | } 43 | 44 | @Override 45 | public void onGaugeRemoved(String s) { 46 | log.info("Gauge removed: {}", s); 47 | } 48 | 49 | @Override 50 | public void onCounterAdded(String s, Counter counter) { 51 | log.info("Counter added: {}", s); 52 | } 53 | 54 | @Override 55 | public void onCounterRemoved(String s) { 56 | log.info("Counter removed: {}", s); 57 | } 58 | 59 | @Override 60 | public void onHistogramAdded(String s, Histogram histogram) { 61 | log.info("Histogram added: {}", s); 62 | } 63 | 64 | @Override 65 | public void onHistogramRemoved(String s) { 66 | log.info("Histogram removed: {}", s); 67 | } 68 | 69 | @Override 70 | public void onMeterAdded(String s, Meter meter) { 71 | log.info("Meter added: {}", s); 72 | } 73 | 74 | @Override 75 | public void onMeterRemoved(String s) { 76 | log.info("Meter removed: {}", s); 77 | } 78 | 79 | @Override 80 | public void onTimerAdded(String s, Timer timer) { 81 | log.info("Timer added: {}", s); 82 | } 83 | 84 | @Override 85 | public void onTimerRemoved(String s) { 86 | log.info("Timer removed: {}", s); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/test/java/com/ryantenney/metrics/spring/MetricAnnotationTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring; 17 | 18 | import static com.ryantenney.metrics.spring.TestUtil.forMetricField; 19 | import static org.junit.Assert.assertNotNull; 20 | import static org.junit.Assert.assertSame; 21 | 22 | import org.junit.Test; 23 | import org.junit.runner.RunWith; 24 | import org.springframework.beans.factory.annotation.Autowired; 25 | import org.springframework.beans.factory.annotation.Qualifier; 26 | import org.springframework.test.context.ContextConfiguration; 27 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 28 | 29 | import com.codahale.metrics.Counter; 30 | import com.codahale.metrics.Histogram; 31 | import com.codahale.metrics.Meter; 32 | import com.codahale.metrics.MetricRegistry; 33 | import com.codahale.metrics.Timer; 34 | import com.codahale.metrics.UniformReservoir; 35 | import com.codahale.metrics.annotation.Metric; 36 | 37 | @RunWith(SpringJUnit4ClassRunner.class) 38 | @ContextConfiguration("classpath:metric-annotation.xml") 39 | public class MetricAnnotationTest { 40 | 41 | @Autowired 42 | @Qualifier("target1") 43 | MetricAnnotationTest.Target target; 44 | 45 | @Autowired 46 | @Qualifier("target2") 47 | MetricAnnotationTest.Target target2; 48 | 49 | @Autowired 50 | MetricRegistry metricRegistry; 51 | 52 | @Test 53 | public void targetIsNotNull() throws Exception { 54 | assertNotNull(target); 55 | assertNotNull(target2); 56 | 57 | assertNotNull(target.theNameForTheMeter); 58 | assertNotNull(target2.theNameForTheMeter); 59 | Meter meter = (Meter) forMetricField(metricRegistry, MetricAnnotationTest.Target.class, "theNameForTheMeter"); 60 | assertSame(target.theNameForTheMeter, meter); 61 | assertSame(target2.theNameForTheMeter, meter); 62 | 63 | assertNotNull(target.timer); 64 | assertNotNull(target2.timer); 65 | Timer timer = (Timer) forMetricField(metricRegistry, MetricAnnotationTest.Target.class, "timer"); 66 | assertSame(target.timer, timer); 67 | assertSame(target2.timer, timer); 68 | 69 | assertNotNull(target.counter); 70 | assertNotNull(target2.counter); 71 | Counter ctr = (Counter) forMetricField(metricRegistry, MetricAnnotationTest.Target.class, "counter"); 72 | assertSame(target.counter, ctr); 73 | assertSame(target2.counter, ctr); 74 | 75 | assertNotNull(target.histogram); 76 | assertNotNull(target2.histogram); 77 | Histogram hist = (Histogram) forMetricField(metricRegistry, MetricAnnotationTest.Target.class, "histogram"); 78 | assertSame(target.histogram, hist); 79 | assertSame(target2.histogram, hist); 80 | 81 | assertNotNull(target.uniformHistogram); 82 | assertNotNull(target2.uniformHistogram); 83 | Histogram uniHist = (Histogram) forMetricField(metricRegistry, MetricAnnotationTest.Target.class, "uniformHistogram"); 84 | assertSame(target.uniformHistogram, uniHist); 85 | assertSame(target2.uniformHistogram, uniHist); 86 | } 87 | 88 | public static class Target { 89 | 90 | @Metric 91 | public Meter theNameForTheMeter; 92 | 93 | @Metric(name = "theNameForTheTimer") 94 | private Timer timer; 95 | 96 | @Metric(name = "group.type.counter", absolute = true) 97 | Counter counter; 98 | 99 | @Metric 100 | Histogram histogram; 101 | 102 | @Metric 103 | Histogram uniformHistogram = new Histogram(new UniformReservoir()); 104 | 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /src/test/java/com/ryantenney/metrics/spring/ProxyTargetClassTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring; 17 | 18 | import org.junit.Assert; 19 | import org.junit.Test; 20 | import org.springframework.beans.factory.BeanCreationException; 21 | import org.springframework.beans.factory.annotation.Autowired; 22 | import org.springframework.context.support.ClassPathXmlApplicationContext; 23 | 24 | import com.codahale.metrics.annotation.Timed; 25 | 26 | /** 27 | * Purpose of test: 28 | * Tests `proxy-target-class` option in `` 29 | */ 30 | public class ProxyTargetClassTest { 31 | 32 | @Test(expected = BeanCreationException.class) 33 | public void negativeContextLoadingTest() { 34 | ClassPathXmlApplicationContext ctx = null; 35 | try { 36 | ctx = new ClassPathXmlApplicationContext("classpath:proxy-target-class-disabled.xml"); 37 | Assert.fail(); 38 | } 39 | finally { 40 | if (ctx != null) { 41 | ctx.close(); 42 | } 43 | } 44 | } 45 | 46 | @Test 47 | public void positiveContextLoadingTest() { 48 | ClassPathXmlApplicationContext ctx = null; 49 | try { 50 | ctx = new ClassPathXmlApplicationContext("classpath:proxy-target-class-enabled.xml"); 51 | Assert.assertNotNull("Expected to be able to get ProxyTargetClass by class.", ctx.getBean(ProxyTargetClass.class)); 52 | Assert.assertNotNull("Expected to be able to get ProxyTargetClass from AutowiredCollaborator.", ctx.getBean(AutowiredCollaborator.class) 53 | .getDependency()); 54 | } 55 | finally { 56 | if (ctx != null) { 57 | ctx.close(); 58 | } 59 | } 60 | } 61 | 62 | /** 63 | * Empty interface to trick Spring. 64 | */ 65 | public interface UselessInterface {} 66 | 67 | public static class ProxyTargetClass implements UselessInterface { 68 | 69 | @Timed 70 | public void timed() {} 71 | 72 | } 73 | 74 | public static class AutowiredCollaborator { 75 | 76 | @Autowired 77 | private ProxyTargetClass dependency; 78 | 79 | public ProxyTargetClass getDependency() { 80 | return dependency; 81 | } 82 | 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /src/test/java/com/ryantenney/metrics/spring/RegisterElementTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring; 17 | 18 | import org.junit.Assert; 19 | import org.junit.Test; 20 | import org.springframework.context.support.ClassPathXmlApplicationContext; 21 | 22 | import com.codahale.metrics.MetricRegistry; 23 | 24 | public class RegisterElementTest { 25 | 26 | @Test 27 | public void testRegisterMetrics() { 28 | ClassPathXmlApplicationContext ctx = null; 29 | try { 30 | ctx = new ClassPathXmlApplicationContext("classpath:register-element-test.xml"); 31 | MetricRegistry registry = ctx.getBean(MetricRegistry.class); 32 | Assert.assertTrue(registry.getMetrics().size() > 0); 33 | for (String metricName : registry.getMetrics().keySet()) { 34 | Assert.assertTrue(metricName.startsWith("jvm.")); 35 | } 36 | } 37 | finally { 38 | if (ctx != null) { 39 | ctx.close(); 40 | } 41 | } 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/test/java/com/ryantenney/metrics/spring/RegistryTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring; 17 | 18 | import org.junit.Assert; 19 | import org.junit.Test; 20 | import org.springframework.context.support.ClassPathXmlApplicationContext; 21 | 22 | import com.codahale.metrics.MetricRegistry; 23 | import com.codahale.metrics.health.HealthCheckRegistry; 24 | 25 | public class RegistryTest { 26 | 27 | @Test 28 | public void testDefaultRegistries() { 29 | ClassPathXmlApplicationContext ctx = null; 30 | try { 31 | ctx = new ClassPathXmlApplicationContext("classpath:default-registries.xml"); 32 | Assert.assertNotNull("Should have a MetricRegistry bean.", ctx.getBean(MetricRegistry.class)); 33 | Assert.assertNotNull("Should have a HealthCheckRegistry bean.", ctx.getBean(HealthCheckRegistry.class)); 34 | } 35 | finally { 36 | if (ctx != null) { 37 | ctx.close(); 38 | } 39 | } 40 | } 41 | 42 | @Test 43 | public void testSuppliedRegistries() { 44 | ClassPathXmlApplicationContext ctx = null; 45 | try { 46 | ctx = new ClassPathXmlApplicationContext("classpath:supplied-registries.xml"); 47 | Assert.assertNotNull("Should have a MetricRegistry bean.", ctx.getBean("metrics", MetricRegistry.class)); 48 | Assert.assertNotNull("Should have a HealthCheckRegistry bean.", ctx.getBean("health", HealthCheckRegistry.class)); 49 | } 50 | finally { 51 | if (ctx != null) { 52 | ctx.close(); 53 | } 54 | } 55 | } 56 | 57 | @Test 58 | public void testCustomRegistries() { 59 | ClassPathXmlApplicationContext ctx = null; 60 | try { 61 | ctx = new ClassPathXmlApplicationContext("classpath:custom-registries.xml"); 62 | Assert.assertSame("Should have a custom MetricRegistry bean.", MetricRegistry.class, ctx.getBean("metrics", MetricRegistry.class).getClass() 63 | .getSuperclass()); 64 | Assert.assertSame("Should have a custom HealthCheckRegistry bean.", HealthCheckRegistry.class, ctx.getBean("health", HealthCheckRegistry.class) 65 | .getClass().getSuperclass()); 66 | } 67 | finally { 68 | if (ctx != null) { 69 | ctx.close(); 70 | } 71 | } 72 | } 73 | 74 | public static class CustomMetricRegistry extends MetricRegistry {} 75 | 76 | public static class CustomHealthCheckRegistry extends HealthCheckRegistry {} 77 | 78 | } 79 | -------------------------------------------------------------------------------- /src/test/java/com/ryantenney/metrics/spring/SharedRegistryTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring; 17 | 18 | import org.junit.Assert; 19 | import org.junit.Test; 20 | import org.springframework.context.support.ClassPathXmlApplicationContext; 21 | 22 | import com.codahale.metrics.MetricRegistry; 23 | import com.codahale.metrics.SharedMetricRegistries; 24 | 25 | public class SharedRegistryTest { 26 | 27 | @Test 28 | public void testDefaultRegistries() { 29 | ClassPathXmlApplicationContext ctx = null; 30 | try { 31 | MetricRegistry instance = new MetricRegistry(); 32 | SharedMetricRegistries.add("SomeSharedRegistry", instance); 33 | ctx = new ClassPathXmlApplicationContext("classpath:shared-registry.xml"); 34 | Assert.assertSame("Should be the same MetricRegistry", instance, ctx.getBean(MetricRegistry.class)); 35 | } 36 | finally { 37 | if (ctx != null) { 38 | ctx.close(); 39 | } 40 | } 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/test/java/com/ryantenney/metrics/spring/TestSuite.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring; 17 | 18 | import org.junit.BeforeClass; 19 | import org.junit.runner.RunWith; 20 | import org.junit.runners.Suite; 21 | import org.junit.runners.Suite.SuiteClasses; 22 | 23 | import com.codahale.metrics.SharedMetricRegistries; 24 | 25 | @RunWith(Suite.class) 26 | // @formatter:off 27 | @SuiteClasses({ 28 | AopFieldInjectionInteractionTest.class, 29 | CovariantReturnTypeTest.class, 30 | EnableMetricsTest.class, 31 | HealthCheckTest.class, 32 | LegacyAnnotationMeteredClassTest.class, 33 | LegacyMetricAnnotationTest.class, 34 | MeteredClassImpementsInterfaceTest.class, 35 | MeteredClassTest.class, 36 | MeteredInterfaceTest.class, 37 | MetricAnnotationTest.class, 38 | ProxyTargetClassTest.class, 39 | RegistryTest.class, 40 | ReporterTest.class, 41 | SharedRegistryTest.class 42 | }) 43 | // @formatter:on 44 | public class TestSuite { 45 | 46 | @BeforeClass 47 | public static void beforeClass() { 48 | SharedMetricRegistries.clear(); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/test/java/com/ryantenney/metrics/spring/benchmarks/MeterBenchmark.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring.benchmarks; 17 | 18 | import com.google.caliper.AfterExperiment; 19 | import com.google.caliper.BeforeExperiment; 20 | import org.springframework.context.annotation.AnnotationConfigApplicationContext; 21 | import org.springframework.context.annotation.Bean; 22 | import org.springframework.context.annotation.Configuration; 23 | 24 | import com.codahale.metrics.Meter; 25 | import com.codahale.metrics.MetricRegistry; 26 | import com.codahale.metrics.annotation.Metered; 27 | import com.google.caliper.Benchmark; 28 | import com.google.caliper.runner.CaliperMain; 29 | import com.ryantenney.metrics.spring.config.annotation.EnableMetrics; 30 | 31 | public class MeterBenchmark { 32 | 33 | public static void main(String[] args) throws Exception { 34 | CaliperMain.main(MeterBenchmark.class, args); 35 | } 36 | 37 | private AnnotationConfigApplicationContext ctx; 38 | private BenchmarkTarget targetBean; 39 | 40 | private Meter meter; 41 | private BenchmarkTarget targetObj; 42 | 43 | @BeforeExperiment 44 | protected void setUp() throws Exception { 45 | ctx = new AnnotationConfigApplicationContext(BenchmarkConfig.class); 46 | ctx.start(); 47 | targetBean = ctx.getBean(BenchmarkTarget.class); 48 | 49 | meter = new MetricRegistry().meter("foobar"); 50 | targetObj = new BenchmarkTarget(); 51 | } 52 | 53 | @AfterExperiment 54 | protected void tearDown() throws Exception { 55 | ctx.stop(); 56 | } 57 | 58 | @Benchmark 59 | public void timeBean(int reps) { 60 | for (int i = 0; i < reps; i++) { 61 | targetBean.mark(); 62 | } 63 | } 64 | 65 | @Benchmark 66 | public void timeNormal(int reps) { 67 | for (int i = 0; i < reps; i++) { 68 | meter.mark(); 69 | targetObj.mark(); 70 | } 71 | } 72 | 73 | @Configuration 74 | @EnableMetrics 75 | public static class BenchmarkConfig { 76 | 77 | @Bean 78 | public BenchmarkTarget createTarget() { 79 | return new BenchmarkTarget(); 80 | } 81 | 82 | } 83 | 84 | public static class BenchmarkTarget { 85 | @Metered 86 | public Object mark() { 87 | return null; 88 | } 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /src/test/java/com/ryantenney/metrics/spring/reporter/FakeReporter.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring.reporter; 17 | 18 | import java.util.SortedMap; 19 | import java.util.concurrent.TimeUnit; 20 | 21 | import com.codahale.metrics.Counter; 22 | import com.codahale.metrics.Gauge; 23 | import com.codahale.metrics.Histogram; 24 | import com.codahale.metrics.Meter; 25 | import com.codahale.metrics.MetricFilter; 26 | import com.codahale.metrics.MetricRegistry; 27 | import com.codahale.metrics.ScheduledReporter; 28 | import com.codahale.metrics.Timer; 29 | 30 | public class FakeReporter extends ScheduledReporter { 31 | 32 | private final MetricRegistry registry; 33 | private final MetricFilter filter; 34 | private final String prefix; 35 | 36 | private long period; 37 | private int calls = 0; 38 | private boolean running = false; 39 | 40 | public FakeReporter(MetricRegistry registry, MetricFilter filter, String prefix, TimeUnit rateUnit, TimeUnit durationUnit) { 41 | super(registry, "test-reporter", filter, rateUnit, durationUnit); 42 | this.registry = registry; 43 | this.filter = filter; 44 | this.prefix = prefix; 45 | } 46 | 47 | public MetricRegistry getRegistry() { 48 | return registry; 49 | } 50 | 51 | public MetricFilter getFilter() { 52 | return filter; 53 | } 54 | 55 | public String getPrefix() { 56 | return prefix; 57 | } 58 | 59 | @Override 60 | public String getRateUnit() { 61 | return super.getRateUnit(); 62 | } 63 | 64 | @Override 65 | public String getDurationUnit() { 66 | return super.getDurationUnit(); 67 | } 68 | 69 | public long getPeriod() { 70 | return period; 71 | } 72 | 73 | public int getCalls() { 74 | return calls; 75 | } 76 | 77 | public boolean isRunning() { 78 | return running; 79 | } 80 | 81 | @Override 82 | public void start(final long period, final TimeUnit unit) { 83 | super.start(period, unit); 84 | this.period = unit.toNanos(period); 85 | this.running = true; 86 | } 87 | 88 | @Override 89 | public void stop() { 90 | super.stop(); 91 | this.running = false; 92 | } 93 | 94 | @Override 95 | @SuppressWarnings("rawtypes") 96 | public void report(SortedMap gauges, SortedMap counters, SortedMap histograms, 97 | SortedMap meters, SortedMap timers) { 98 | calls++; 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /src/test/java/com/ryantenney/metrics/spring/reporter/FakeReporterElementParser.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring.reporter; 17 | 18 | import static com.ryantenney.metrics.spring.reporter.FakeReporterFactoryBean.*; 19 | 20 | public class FakeReporterElementParser extends AbstractReporterElementParser { 21 | 22 | @Override 23 | public String getType() { 24 | return "fake"; 25 | } 26 | 27 | @Override 28 | protected Class getBeanClass() { 29 | return FakeReporterFactoryBean.class; 30 | } 31 | 32 | @Override 33 | protected void validate(ValidationContext c) { 34 | c.require(PERIOD, DURATION_STRING_REGEX, "Period is required and must be in the form '\\d+(ns|us|ms|s|m|h|d)'"); 35 | 36 | c.optional(RATE_UNIT, TIMEUNIT_STRING_REGEX, "Rate unit must be one of the enum constants from java.util.concurrent.TimeUnit"); 37 | c.optional(DURATION_UNIT, TIMEUNIT_STRING_REGEX, "Duration unit must be one of the enum constants from java.util.concurrent.TimeUnit"); 38 | 39 | c.optional(PREFIX); 40 | c.optional(PREFIX_SUPPLIER_REF); 41 | if (c.has(PREFIX) && c.has(PREFIX_SUPPLIER_REF)) { 42 | c.reject(PREFIX_SUPPLIER_REF, "Reporter element may not specify both the 'prefix' and 'prefix-supplier-ref' attributes"); 43 | } 44 | 45 | c.optional(FILTER_PATTERN); 46 | c.optional(FILTER_REF); 47 | if (c.has(FILTER_PATTERN) && c.has(FILTER_REF)) { 48 | c.reject(FILTER_REF, "Reporter element may not specify both the 'filter' and 'filter-ref' attributes"); 49 | } 50 | 51 | c.rejectUnmatchedProperties(); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/test/java/com/ryantenney/metrics/spring/reporter/FakeReporterFactoryBean.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.ryantenney.metrics.spring.reporter; 17 | 18 | import java.util.concurrent.TimeUnit; 19 | 20 | public class FakeReporterFactoryBean extends AbstractScheduledReporterFactoryBean { 21 | 22 | // Required 23 | public static final String PERIOD = "period"; 24 | public static final String DURATION_UNIT = "duration-unit"; 25 | public static final String RATE_UNIT = "rate-unit"; 26 | 27 | @Override 28 | public Class getObjectType() { 29 | return FakeReporter.class; 30 | } 31 | 32 | @Override 33 | protected FakeReporter createInstance() { 34 | TimeUnit durationUnit = getProperty(DURATION_UNIT, TimeUnit.class); 35 | TimeUnit rateUnit = getProperty(RATE_UNIT, TimeUnit.class); 36 | 37 | return new FakeReporter(getMetricRegistry(), getMetricFilter(), getPrefix(), rateUnit, durationUnit); 38 | } 39 | 40 | @Override 41 | protected long getPeriod() { 42 | return convertDurationString(getProperty(PERIOD)); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/test/resources/META-INF/services/com.ryantenney.metrics.spring.reporter.ReporterElementParser: -------------------------------------------------------------------------------- 1 | com.ryantenney.metrics.spring.reporter.FakeReporterElementParser -------------------------------------------------------------------------------- /src/test/resources/aop-field-injection-interaction.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/test/resources/covariant-return-type.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/test/resources/custom-registries.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/test/resources/default-registries.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/test/resources/fake-reporter-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/test/resources/health-check.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/test/resources/injected-metrics.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/test/resources/legacy-annotation-metered-class.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/test/resources/legacy-metric-annotation.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 18 | 19 | 20 | 21 | 22 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/test/resources/metered-class.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/test/resources/metered-interface-impl.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/test/resources/metered-interface.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/test/resources/metric-annotation.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/test/resources/proxy-target-class-disabled.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/test/resources/proxy-target-class-enabled.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/test/resources/register-element-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/test/resources/reporter-placeholder-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/test/resources/shared-registry.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/test/resources/supplied-registries.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | --------------------------------------------------------------------------------