CDI portable extension for Dropwizard Metrics compliant with JSR 346: Contexts and Dependency Injection for JavaTM EE 1.2.
Metrics CDI provides support for the Metrics annotations in CDI enabled environments. It implements the contract specified by these annotations with the following level of functionality:
- Intercepts invocations of bean constructors, methods and public methods of bean classes annotated with
@Counted
,@ExceptionMetered
,@Metered
and@Timed
, - Creates
Gauge
andCachedGauge
instances for bean methods annotated with@Gauge
and@CachedGauge
respectively, - Injects
Counter
,Gauge
,Histogram
,Meter
andTimer
instances, - Registers or retrieves the produced
Metric
instances in the resolvedMetricRegistry
bean, - Declares automatically a default
MetricRegistry
bean if no one exists in the CDI container, - Registers
HealthCheck
beans with a provided or automatically configuredHealthCheckRegistry
bean instance.
Metrics CDI is compatible with Metrics version 3.1.0
+.
Add the metrics-cdi
library as a dependency:
<dependency>
<groupId>io.astefanutti.metrics.cdi</groupId>
<artifactId>metrics-cdi</artifactId>
<version>1.6.0</version>
</dependency>
Besides depending on Metrics (metrics-core
and metrics-annotation
modules), Metrics CDI requires a CDI enabled environment running in Java 8 or greater.
Metrics CDI is currently successfully tested with the following containers:
Container | Version | Environment |
---|---|---|
Weld | 2.4.3.Final |
Java SE 8 / CDI 1.2 |
Weld | 3.0.3.Final |
Java SE 8 / CDI 2.0 |
OpenWebBeans | 1.7.3 |
Java SE 8 / CDI 1.2 |
Jetty | 9.4.5 |
Servlet 3.1 |
WildFly 10 | 10.1.0.Final |
Java EE 7 |
Metrics CDI activates the Metrics AOP Instrumentation for beans annotated with Metrics annotations and automatically registers the corresponding Metric
instances in the Metrics registry resolved for the CDI application. The registration of these Metric
instances happens each time such a bean gets instantiated. Besides, Metric
instances can be retrieved from the Metrics registry by declaring metrics injection points.
The metrics registration mechanism can be used to customize the Metric
instances that get registered. Besides, the Metrics registry resolution mechanism can be used for the application to provide a custom MetricRegistry
instance.
Health checks support is automatically activated when the metrics-healthchecks
optional dependency is present in the classpath.
Metrics comes with the metrics-annotation
module that contains a set of annotations and provides a standard way to integrate Metrics with frameworks supporting Aspect Oriented Programming (AOP). These annotations are supported by Metrics CDI that implements their contract as documented in their Javadoc.
For example, a method of a bean can be annotated so that its execution can be monitored using Metrics:
import com.codahale.metrics.annotation.Timed;
class TimedMethodBean {
@Timed
void timedMethod() {
// Timer name => TimedMethodBean.timedMethod
}
}
or the bean class can be annotated directly so that all its public methods get monitored:
import com.codahale.metrics.annotation.Metered;
@Metered
public class MeteredClassBean {
public void meteredMethod() {
// Meter name => MeteredClassBean.meteredMethod
}
}
or the bean constructor can be annotated so that its instantiations get monitored:
import com.codahale.metrics.annotation.Counted;
class CountedConstructorBean {
@Counted
CountedConstructorBean() {
// Counter name => CountedConstructorBean.CountedConstructorBean
}
}
The name
and absolute
attributes available on every Metrics annotation can be used to customize the name of the Metric
instance that gets registered in the Metrics registry. The default naming convention being the annotated member simple name relative to the declaring class fully qualified name as illustrated in the above examples.
Metric
instances can be retrieved from the Metrics registry by declaring an injected field, e.g.:
import com.codahale.metrics.Timer;
import javax.inject.Inject;
class TimerBean {
@Inject
private Timer timer; // Timer name => TimerBean.Timer
}
Metric
instances can be injected similarly as parameters of any initializer method or bean constructor, e.g.:
import com.codahale.metrics.Timer;
import javax.inject.Inject;
class TimerBean {
private final Timer timer;
@Inject
private TimerBean(Timer timer) { // Timer name => TimerBean.Timer
this.timer = timer;
}
}
In the above example, the -parameters
compiler option is required to get access to injected parameter name. Indeed, access to parameter names at runtime has been introduced with JEP-118. More information can be found in Obtaining Names of Method Parameters from the Java tutorials. To work around that limitation, or to declare a specific name, the @Metric
annotation can be used as documented hereafter.
In order to provide metadata for the Metric
instantiation and resolution, the injection point can be annotated with the @Metric
annotation, e.g., with an injected field:
import com.codahale.metrics.Timer;
import com.codahale.metrics.annotation.Metric;
import javax.inject.Inject;
@Inject
@Metric(name = "timerName", absolute = true)
private Timer timer; // Timer name => timerName
or when using a bean constructor:
import com.codahale.metrics.Timer;
import com.codahale.metrics.annotation.Metric;
import javax.inject.Inject;
class TimerBean {
private final Timer timer;
@Inject
private TimerBean(@Metric(name = "timerName", absolute = true) Timer timer) {
// Timer name => timerName
this.timer = timer;
}
}
While Metrics CDI automatically registers Metric
instances during the Metrics AOP instrumentation, it may be necessary for an application to explicitly provide the Metric
instances to register. For example, to provide particular Reservoir
implementations to histograms or timers, e.g. with a producer field:
import com.codahale.metrics.SlidingTimeWindowReservoir;
import com.codahale.metrics.Timer;
import com.codahale.metrics.annotation.Metric;
import com.codahale.metrics.annotation.Timed;
import javax.enterprise.inject.Produces;
class TimedMethodBean {
@Produces
@Metric(name = "customTimer") // Timer name => TimedMethodBean.customTimer
Timer Timer = new Timer(new SlidingTimeWindowReservoir(1L, TimeUnit.MINUTES));
@Timed(name = "customTimer")
void timedMethod() {
// Timer name => TimedMethodBean.customTimer
}
}
Another use case is to register custom gauges, e.g. with a producer method:
class CacheHitRatioBean {
@Inject
private Meter hits;
@Timed(name = "calls")
public void cachedMethod() {
if (hit) hits.mark();
}
@Produces
@Metric(name = "cache-hits")
private Gauge<Double> cacheHitRatioGauge(Meter hits, Timer calls) {
return () -> Ratio.of(hits.getCount(), calls.getCount()).getValue();
}
}
Metrics CDI automatically registers a MetricRegistry
bean into the CDI container to register any Metric
instances produced. That default MetricRegistry
bean can be injected using standard CDI typesafe resolution, for example, by declaring an injected field:
import com.codahale.metrics.MetricRegistry;
import javax.inject.Inject;
@Inject
private MetricRegistry registry;
or by declaring a bean constructor:
import com.codahale.metrics.MetricRegistry;
import javax.inject.Inject;
class MetricRegistryBean {
private final MetricRegistry registry;
@Inject
private MetricRegistryBean(MetricRegistry registry) {
this.registry = registry;
}
}
Otherwise, Metrics CDI uses any MetricRegistry
bean declared in the CDI container with the built-in default qualifier @Default
so that a custom MetricRegistry
can be provided. For example, that custom MetricRegistry
can be declared with a producer field:
import com.codahale.metrics.MetricRegistry;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Produces;
@Produces
@ApplicationScoped
private final MetricRegistry registry = new MetricRegistry();
or with a producer method:
import com.codahale.metrics.MetricRegistry;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Produces;
class MetricRegistryFactoryBean {
@Produces
@ApplicationScoped
private MetricRegistry metricRegistry() {
return new MetricRegistry();
}
}
Metrics CDI automatically registers a HealthCheckRegistry
bean into the CDI container. This follows the same resolution mechanism as that of the Metrics registry, so that the application may provide a custom HealthCheckRegistry
instance.
Metrics CDI then automatically registers any HealthCheck
instance with the configured HealthCheckRegistry
instance.
A HealthCheck
bean can be declared as any CDI bean, e.g. with a bean class:
import com.codahale.metrics.health.HealthCheck;
import javax.inject.Inject;
import javax.inject.Named;
@Named("databaseHealthCheck")
class DatabaseHealthCheck extends HealthCheck {
@Inject
private Database database;
@Override
protected Result check() {
return database.ping()
? Result.healthy()
: Result.unhealthy("Can't ping database!");
}
}
Metrics CDI fires a MetricsConfiguration
event at deployment time that can be used by the application to configure it, e.g.:
import io.astefanutti.metrics.cdi.MetricsConfiguration;
import javax.enterprise.event.Observes;
class MetricsCdiConfiguration {
static void configure(@Observes MetricsConfiguration metrics) {
// Use absolute name globally
metrics.useAbsoluteName(true);
// Use a uniform reservoir globally
metrics.reservoirFunction((name, type) -> Optional.of(new UniformReservoir());
}
}
Note that this event can only be used within the context of the observer method invocation. Any attempt to call one of its methods outside of that context will result in an IllegalStateException
to be thrown.
CDI 1.2 leverages on Java Interceptors Specification 1.2 to provide the ability to associate interceptors to beans via typesafe interceptor bindings. Interceptors are a mean to separate cross-cutting concerns from the business logic and Metrics CDI is relying on interceptors to implement the support of Metrics annotations in a CDI enabled environment.
CDI 1.2 sets additional restrictions about the type of bean to which an interceptor can be bound. From a Metrics CDI end-user perspective, that implies that the managed beans to be monitored with Metrics (i.e. having at least one member method annotated with one of the Metrics annotations) must be proxyable bean types, as defined in Unproxyable bean types, that are:
- Classes which don’t have a non-private constructor with no parameters,
- Classes which are declared
final
,- Classes which have non-static, final methods with public, protected or default visibility,
- Primitive types,
- And array types.
Copyright © 2013, Antonin Stefanutti
Published under Apache Software License 2.0, see LICENSE