https://github.com/astefanutti/metrics-cdi
CDI extension for Dropwizard Metrics
https://github.com/astefanutti/metrics-cdi
cdi dropwizard java javaee metrics
Last synced: 11 months ago
JSON representation
CDI extension for Dropwizard Metrics
- Host: GitHub
- URL: https://github.com/astefanutti/metrics-cdi
- Owner: astefanutti
- License: apache-2.0
- Created: 2013-10-20T13:15:52.000Z (over 12 years ago)
- Default Branch: master
- Last Pushed: 2022-01-04T16:31:59.000Z (about 4 years ago)
- Last Synced: 2025-03-27T23:51:10.349Z (11 months ago)
- Topics: cdi, dropwizard, java, javaee, metrics
- Language: Java
- Homepage:
- Size: 765 KB
- Stars: 77
- Watchers: 9
- Forks: 19
- Open Issues: 4
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# CDI Extension for Metrics
[![Build Status][Travis badge]][Travis build] [![Coverage Status][Coveralls badge]][Coveralls build] [![Maven Central][Maven Central badge]][Maven Central build]
[Travis badge]: https://travis-ci.org/astefanutti/metrics-cdi.svg
[Travis build]: https://travis-ci.org/astefanutti/metrics-cdi
[Coveralls badge]: https://coveralls.io/repos/astefanutti/metrics-cdi/badge.svg
[Coveralls build]: https://coveralls.io/github/astefanutti/metrics-cdi
[Maven Central badge]: https://img.shields.io/maven-central/v/io.astefanutti.metrics.cdi/metrics-cdi.svg
[Maven Central build]: https://repo1.maven.org/maven2/io/astefanutti/metrics/cdi/metrics-cdi/1.6.0/
[CDI][] portable extension for Dropwizard [Metrics][] compliant with [JSR 346: Contexts and Dependency Injection for JavaTM EE 1.2][JSR 346 1.2].
[CDI]: http://www.cdi-spec.org/
[Metrics]: https://metrics.dropwizard.io/
[JSR 346]: https://jcp.org/en/jsr/detail?id=346
[JSR 346 1.1]: https://jcp.org/aboutJava/communityprocess/final/jsr346/index.html
[JSR 346 1.2]: https://jcp.org/aboutJava/communityprocess/mrel/jsr346/index.html
[JSR 365]: https://jcp.org/en/jsr/detail?id=365
[CDI 1.2]: https://docs.jboss.org/cdi/spec/1.2/cdi-spec.html
## About
_Metrics CDI_ provides support for the [_Metrics_ annotations][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`][] and [`CachedGauge`][] instances for bean methods annotated with [`@Gauge`][] and [`@CachedGauge`][] respectively,
+ Injects [`Counter`][], [`Gauge`][], [`Histogram`][], [`Meter`][] and [`Timer`][] instances,
+ Registers or retrieves the produced [`Metric`][] instances in the resolved [`MetricRegistry`][] bean,
+ Declares automatically a default [`MetricRegistry`][] bean if no one exists in the CDI container,
+ Registers [`HealthCheck`][] beans with a provided or automatically configured [`HealthCheckRegistry`][] bean instance.
_Metrics CDI_ is compatible with _Metrics_ version `3.1.0`+.
[Metrics annotations]: https://metrics.dropwizard.io/4.0.0/apidocs/com/codahale/metrics/annotation/package-summary.html
[`@CachedGauge`]: https://metrics.dropwizard.io/4.0.0/apidocs/com/codahale/metrics/annotation/CachedGauge.html
[`@Counted`]: https://metrics.dropwizard.io/4.0.0/apidocs/com/codahale/metrics/annotation/Counted.html
[`@ExceptionMetered`]: https://metrics.dropwizard.io/4.0.0/apidocs/com/codahale/metrics/annotation/ExceptionMetered.html
[`@Gauge`]: https://metrics.dropwizard.io/4.0.0/apidocs/com/codahale/metrics/annotation/Gauge.html
[`@Metered`]: https://metrics.dropwizard.io/4.0.0/apidocs/com/codahale/metrics/annotation/Gauge.html
[`@Timed`]: https://metrics.dropwizard.io/4.0.0/apidocs/com/codahale/metrics/annotation/Timed.html
[`CachedGauge`]: https://metrics.dropwizard.io/4.0.0/apidocs/com/codahale/metrics/CachedGauge.html
[`Counter`]: https://metrics.dropwizard.io/4.0.0/apidocs/com/codahale/metrics/Counter.html
[`Gauge`]: https://metrics.dropwizard.io/4.0.0/apidocs/com/codahale/metrics/Gauge.html
[`Histogram`]: https://metrics.dropwizard.io/4.0.0/apidocs/com/codahale/metrics/Histogram.html
[`Meter`]: https://metrics.dropwizard.io/4.0.0/apidocs/com/codahale/metrics/Meter.html
[`Metric`]: https://metrics.dropwizard.io/4.0.0/apidocs/com/codahale/metrics/Metric.html
[`Timer`]: https://metrics.dropwizard.io/4.0.0/apidocs/com/codahale/metrics/Timer.html
[`MetricRegistry`]: https://metrics.dropwizard.io/4.0.0/apidocs/com/codahale/metrics/MetricRegistry.html
[`HealthCheck`]: https://metrics.dropwizard.io/4.0.0/apidocs/com/codahale/metrics/health/HealthCheck.html
[`HealthCheckRegistry`]: https://metrics.dropwizard.io/4.0.0/apidocs/com/codahale/metrics/health/HealthCheckRegistry.html
## Getting Started
#### Using Maven
Add the `metrics-cdi` library as a dependency:
```xml
io.astefanutti.metrics.cdi
metrics-cdi
1.6.0
```
#### Required Dependencies
Besides depending on _Metrics_ (`metrics-core` and `metrics-annotation` modules), _Metrics CDI_ requires a CDI enabled environment running in Java 8 or greater.
#### Supported Containers
_Metrics CDI_ is currently successfully tested with the following containers:
| Container | Version | Environment |
| --------------------- | -------------- | ---------------------------------- |
| [Weld][] | `2.4.3.Final` | Java SE 8 / [CDI 1.2][JSR 346 1.2] |
| [Weld][] | `3.0.3.Final` | Java SE 8 / [CDI 2.0][JSR 365] |
| [OpenWebBeans][] | `1.7.3` | Java SE 8 / [CDI 1.2][JSR 346 1.2] |
| [Jetty][] | `9.4.5` | [Servlet 3.1][] |
| [WildFly 10][WildFly] | `10.1.0.Final` | [Java EE 7][] |
[Weld]: https://weld.cdi-spec.org/
[OpenWebBeans]: https://openwebbeans.apache.org/
[Jetty]: https://www.eclipse.org/jetty/
[WildFly]: http://www.wildfly.org/
[Servlet 3.1]: https://jcp.org/en/jsr/detail?id=340
[Java EE 7]: https://jcp.org/en/jsr/detail?id=342
## Usage
_Metrics CDI_ activates the [_Metrics_ AOP Instrumentation](#metrics-aop-instrumentation) for beans annotated with [_Metrics_ annotations][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](#metrics-injection).
The [metrics registration](#metrics-registration) mechanism can be used to customize the `Metric` instances that get registered. Besides, the [_Metrics_ registry resolution](#metrics-registry-resolution) mechanism can be used for the application to provide a custom [`MetricRegistry`] instance.
[Health checks](#health-checks) support is automatically activated when the `metrics-healthchecks` optional dependency is present in the classpath.
[_Metrics_ registry]: https://metrics.dropwizard.io/4.0.0/getting-started.html#the-registry
#### Metrics AOP Instrumentation
_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.
[`metrics-annotation`]: https://github.com/dropwizard/metrics/tree/4.0.0-docs/metrics-annotation
For example, a method of a bean can be annotated so that its execution can be monitored using _Metrics_:
```java
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:
```java
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:
```java
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.
[bean class]: https://docs.jboss.org/cdi/spec/1.2/cdi-spec.html#what_classes_are_beans
[bean constructor]: https://docs.jboss.org/cdi/spec/1.2/cdi-spec.html#bean_constructors
#### Metrics Injection
`Metric` instances can be retrieved from the _Metrics_ registry by declaring an [injected field][], e.g.:
```java
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.:
```java
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.
[JEP-118]: http://openjdk.java.net/jeps/118
[Obtaining Names of Method Parameters]: https://docs.oracle.com/javase/tutorial/reflect/member/methodparameterreflection.html
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][]:
```java
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][]:
```java
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;
}
}
```
[injected field]: https://docs.jboss.org/cdi/spec/1.2/cdi-spec.html#injected_fields
[initializer method]: https://docs.jboss.org/cdi/spec/1.2/cdi-spec.html#initializer_methods
#### Metrics Registration
While _Metrics CDI_ automatically registers `Metric` instances during the [_Metrics_ AOP instrumentation](#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][]:
```java
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][]:
```java
class CacheHitRatioBean {
@Inject
private Meter hits;
@Timed(name = "calls")
public void cachedMethod() {
if (hit) hits.mark();
}
@Produces
@Metric(name = "cache-hits")
private Gauge cacheHitRatioGauge(Meter hits, Timer calls) {
return () -> Ratio.of(hits.getCount(), calls.getCount()).getValue();
}
}
```
[gauges]: https://metrics.dropwizard.io/4.0.0/manual/core.html#gauges
[histograms]: https://metrics.dropwizard.io/4.0.0/manual/core.html#histograms
[timers]: https://metrics.dropwizard.io/4.0.0/manual/core.html#timers
[producer field]: https://docs.jboss.org/cdi/spec/1.2/cdi-spec.html#producer_field
[producer method]: https://docs.jboss.org/cdi/spec/1.2/cdi-spec.html#producer_method
#### Metrics Registry Resolution
_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][]:
```java
import com.codahale.metrics.MetricRegistry;
import javax.inject.Inject;
@Inject
private MetricRegistry registry;
```
or by declaring a [bean constructor][]:
```java
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][]:
```java
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][]:
```java
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();
}
}
```
[typesafe resolution]: https://docs.jboss.org/cdi/spec/1.2/cdi-spec.html#typesafe_resolution
[built-in _default_ qualifier]: https://docs.jboss.org/cdi/spec/1.2/cdi-spec.html#builtin_qualifiers
#### Health Checks
_Metrics CDI_ automatically registers a `HealthCheckRegistry` bean into the CDI container. This follows the same resolution mechanism as that of the [_Metrics_ registry](#metrics-registry-resolution), 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][]:
```java
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 Configuration
_Metrics CDI_ fires a `MetricsConfiguration` event at deployment time that can be used by the application to configure it, e.g.:
```java
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.
## Limitations
[CDI 1.2][] leverages on [Java Interceptors Specification 1.2][] to provide the ability to [associate interceptors to beans][Binding an interceptor to a bean] 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.
[Java Interceptors Specification 1.2]: https://download.oracle.com/otndocs/jcp/interceptors-1_2-mrel2-eval-spec/
[Binding an interceptor to a bean]: https://docs.jboss.org/cdi/spec/1.2/cdi-spec.html#binding_interceptor_to_bean
[Unproxyable bean types]: https://docs.jboss.org/cdi/spec/1.2/cdi-spec.html#unproxyable
## License
Copyright © 2013, Antonin Stefanutti
Published under Apache Software License 2.0, see LICENSE