Giter Club home page Giter Club logo

metrics-aspectj's Introduction

AspectJ for Metrics

Build Status Coverage Status Maven Central

AspectJ integration for Dropwizard Metrics with optional Expression Language 3.0 (JSR-341) support.

About

Metrics AspectJ provides support for the Metrics annotations in Java SE environments using AspectJ to perform AOP instrumentation. It implements the contract specified by these annotations with the following level of functionality:

Metrics AspectJ is compatible with Metrics version 3.0.0+ and requires Java 6 or higher.

Getting Started

Using Maven

Add the metrics-aspectj library as a dependency:

<dependency>
    <groupId>io.astefanutti.metrics.aspectj</groupId>
    <artifactId>metrics-aspectj</artifactId>
    <version>1.2.0</version>
</dependency>

And configure the maven-aspectj-plugin to compile-time weave (CTW) the metrics-aspectj aspects into your project:

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>aspectj-maven-plugin</artifactId>
    <configuration>
        <aspectLibraries>
            <aspectLibrary>
                <groupId>io.astefanutti.metrics.aspectj</groupId>
                <artifactId>metrics-aspectj</artifactId>
            </aspectLibrary>
        </aspectLibraries>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>compile</goal>
            </goals>
        </execution>
    </executions>
</plugin>

More information can be found in the Maven AspectJ plugin documentation.

Using Ant

Use the AjcTask (iajc) Ant task:

<target name="{target}" >
    <iajc sourceroots="${basedir}/src"
          classpath="${basedir}/lib/aspectjrt.jar"
          outjar="${basedir}/build/${ant.project.name}.jar">
        ...
        <aspectpath>
            <pathelement location="${basedir}/lib/metrics-aspectj.jar"/>
        </aspectpath>
        ...
    </iajc>
</target>

Other options are detailed in the AspectJ Ant tasks documentation.

Using Gradle

A working gradle example is available, but each integration point is described here.

build.gradle snippets
buildscript {
    // ensure the gradle-aspectj integration is w/i the build classpath
    dependencies {
        classpath 'nl.eveoh:gradle-aspectj:1.6'
    }
}

// specify the aspectjVersion, used by gradle-aspectj
project.ext {
    aspectjVersion = '1.8.10'
}

// specify the Dropwizard Metrics version (metricsVer)
//  and the aspect-oriented metrics version (metricsAspectVer, this solution)
ext {
    metricsVer = '3.2.2'
    metricsAspectVer = '1.2.0'
}

// via the gradle-aspectj integration, run "aspect weaving"
apply plugin: 'aspectj'

// ensure Dropwizard Metrics as well as the aspect-oriented metrics (astefanutti.metrics.aspectj)
//  runtime dependencies of your solution are satisfied.
dependencies {
    compile "io.astefanutti.metrics.aspectj:metrics-aspectj:${metricsAspectVer}"
    // add a path for the gradle-aspectj "aspect weaving" (AspectJ Compiler compile)
    aspectpath "io.astefanutti.metrics.aspectj:metrics-aspectj:${metricsAspectVer}"

    compile "io.dropwizard.metrics:metrics-core:${metricsVer}"
    compile "io.dropwizard.metrics:metrics-annotation:${metricsVer}"
}

Using the AspectJ Compiler

The AspectJ compiler can be used directly by executing the following command:

ajc -aspectpath metrics-aspectj.jar [Options] [file...]

More information can be found in the AspectJ compiler / weaver documentation.

Required Dependencies

Besides depending on Metrics (metrics-core and metrics-annotation modules), Metrics AspectJ requires the AspectJ aspectjrt module:

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
</dependency>

These three modules are transitive dependencies of the metrics-aspectj Maven module.

Alternatively, the metrics-aspectj-deps artifact that re-packages the metrics-annotation and the aspectjrt modules can be used so that the only required dependency is metrics-core:

<dependency>
    <groupId>io.astefanutti.metrics.aspectj</groupId>
    <artifactId>metrics-aspectj-deps</artifactId>
</dependency>

Optional Dependencies

In addition to that, Metrics AspectJ optional support of EL 3.0 expression for MetricRegistry resolution and Metric name evaluation requires an implementation of Expression Language 3.0 (JSR-341) to be present at runtime. For example, the metrics-aspectj-el module is using the GlassFish reference implementation as test dependency for its unit tests execution:

<dependency>
    <groupId>org.glassfish</groupId>
    <artifactId>javax.el</artifactId>
</dependency>

Usage

Metrics AspectJ Activation

In order to activate Metrics AspectJ for a particular class, it must be annotated with the @Metrics annotation:

import com.codahale.metrics.annotation.Timed;

import io.astefanutti.metrics.aspectj.Metrics;

@Metrics
class TimedMethod {

    @Timed(name = "timerName")
    void timedMethod() {} // Timer name => TimedMethod.timerName
}

At weaving time, Metrics AspectJ will detect the @Metrics annotation, scan all the declared methods of the target class that are annotated with Metrics annotations, then create and register the corresponding Metric instances and weave its aspects around these methods. At runtime, these Metric instances will eventually get called according to the Metrics annotations specification.

Note that Metrics annotations won't be inherited if declared on an interface or a parent class method. More details are available in the Limitations section.

The Metrics Annotations

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 AspectJ that implements their contract as documented in their Javadoc.

For example, a method can be annotated with the @Timed annotation so that its execution can be monitored using Metrics:

import com.codahale.metrics.annotation.Timed;

import io.astefanutti.metrics.aspectj.Metrics;

@Metrics
class TimedMethod {

    @Timed(name = "timerName")
    void timedMethod() {} // Timer name => TimedMethod.timerName
}

In that example, Metrics AspectJ will instrument all the constructors of the TimedMethod class by injecting Java bytecode that will automatically create a Timer instance with the provided name (or retrieve an existing Timer with the same name already registered in the MetricRegistry) right after the instantiation of the TimedMethod class and inline the method invocation around with the needed code to time the method execution using that Timer instance.

A static method can also be annotated with the @Timed annotation so that its execution can be monitored using Metrics:

import com.codahale.metrics.annotation.Timed;

import io.astefanutti.metrics.aspectj.Metrics;

@Metrics
class TimedMethod {

    @Timed(name = "timerName")
    static void timedStaticMethod() {} // Timer name => TimedMethod.timerName
}

In that example, Metrics AspectJ will instrument the TimedMethod class so that, when it's loaded, a Timer instance with the provided name will be created (or an existing Timer with the same name already registered in the MetricRegistry will be retrieved) and inline the method invocation around with the needed code to time the method execution using that Timer instance.

Optionally, the Metric name can be resolved with an EL expression that evaluates to a String:

import com.codahale.metrics.annotation.Timed;

import io.astefanutti.metrics.aspectj.Metrics;

@Metrics
class TimedMethod {

    private long id;

    public long getId() {
        return id;
    }

    @Timed(name = "timerName ${this.id}")
    void timedMethod() {} // Timer name => TimedMethod.timerName <id>
}

In that example, Metrics AspectJ will automatically create a Timer instance (respectively retrieve an existing Timer instance with the same name already registered in the MetricRegistry) right after the instantiation of the TimedMethod class and evaluate the EL expression based on the value of the id attribute of that newly created TimedMethod instance to name the Timer instance (respectively resolve the Timer instance registered in the MetricRegistry). If the value of the id attribute changes over time, the name of the Timer instance won't be re-evaluated.

Note that these annotations won't be inherited if they are placed on interface or parent class methods. Indeed, according to the Java language specification, non-type annotations are not inherited. It is discussed in more details in the Limitations section.

Metrics Registry Resolution

The Metrics.registry annotation attribute provides the way to declare the MetricRegistry to register the generated Metric instances into. Its value can either be a string literal that identifies a MetricRegistry accessible by name from the SharedMetricRegistries class or a valid EL expression that evaluates to the registry name or the registry instance. The resultant MetricRegistry is used to register the Metric instantiated into each time a Metrics annotation is present on that class methods. It defaults to the string literal metrics-registry.

The MetricRegistry can thus be resolved by name relying on the SharedMetricRegistries.getOrCreate(String name) method:

import com.codahale.metrics.annotation.Metered;

import io.astefanutti.metrics.aspectj.Metrics;

@Metrics(registry = "registryName")
class MeteredMethodWithRegistryByName {

    @Metered(name = "meterName")
    void meteredMethod() {} // Registry => SharedMetricRegistries.getOrCreate("registryName")
}

Or with an EL expression that evaluates to a bean property of type MetricRegistry:

import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.annotation.Metered;

import io.astefanutti.metrics.aspectj.Metrics;

@Metrics(registry = "${this.registry}")
class MeteredMethodWithRegistryFromProperty {

    final MetricRegistry registry;

    MeteredMethodWithRegistryFromProperty(MetricRegistry registry) {
        this.registry = registry;
    }

    MetricRegistry getRegistry() {
        return registry;
    }

    @Metered(name = "meterName")
    void meteredMethod() {} // Registry => this.getRegistry()
}

Or with an EL expression that evaluates to a String. In that case the registry is resolved by name using the SharedMetricRegistries.getOrCreate(String name) method.

Limitations

The Metrics annotations are not inherited whether these are declared on a parent class or an implemented interface method. The root causes of that limitation, according to the Java language specification, are:

  • Non-type annotations are not inherited,
  • Annotations on types are only inherited if they have the @Inherited meta-annotation,
  • Annotations on interfaces are not inherited irrespective to having the @Inherited meta-annotation.

See the @Inherited Javadoc and Annotation types from the Java language specification for more details.

AspectJ follows the Java language specification and has documented to what extent it's impacted in Annotation inheritance and Annotation inheritance and pointcut matching. There would have been ways of working around that though:

  • That would have been working around the Java language specification in the first place,
  • Plus that would have required to rely on a combination of Expression-based pointcuts, Runtime type matching and Reflective access to define conditional pointcut expressions which:
    • Would have widen the scope of matching joint points thus introducing side-effects in addition to being inefficient,
    • Would have been evaluated at runtime for each candidate join point relying on the Java Reflection API thus impacting the application performance and incidentally voiding the non-intrusive benefit of AOP in a larger sense.

Spring AOP vs. AspectJ

Spring AOP and AspectJ provides Aspect Oriented Programming (AOP) in two very different ways:

  • AspectJ provides a full-fledged aspect definition and support both Compile Time Weaving (CTW) and Load Time Weaving (LTW) (with a Java agent) and implements AOP with class instrumentation (byte code manipulation),
  • Spring AOP does not support the whole AspectJ aspect definition and does not support Compile Time Weaving,
  • Spring AOP implements AOP either using (see Spring proxying mechanisms):
    • JDK dynamic proxies, which add little runtime overhead, clutter stack traces and can be incompatible with other Spring functionality like Spring JMX (for dynamic MBean export for example),
    • Or CGLIB (byte code manipulation), that has to be added as a runtime dependency:
      • It dynamically extends classes thus it is incompatible with final classes or methods,
      • CGLIB development isn't active, Hibernate has been deprecating it in favor of Javassist (see Deprecated CGLIB support),
  • AJDT (AspectJ Development Tools) provides deep integration between AspectJ and the Eclipse platform which is not possible with Spring AOP due to the runtime / dynamic nature of its AOP implementation.

Further details can be found in Choosing which AOP declaration style to use from the Spring framework documentation. The Spring AOP vs AspectJ question on Stack Overflow provides some insights as well.

License

Copyright © 2013-2016, Antonin Stefanutti

Published under Apache Software License 2.0, see LICENSE

metrics-aspectj's People

Contributors

astefanutti avatar bitdeli-chef avatar dependabot[bot] avatar efenderbosch avatar erikthered avatar gresrun avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

metrics-aspectj's Issues

About metrics reporter

After integrating metrics-aspectj, but there is no reporter.
If not use metrics annotation, it can be used by

ConsoleReporter.forRegistry(metricRegistry).build().start(15, TimeUnit.SECONDS)

can i get the metricRegistry from metrics-aspectj?
please offer a proposal, thx

@Metered doesn't execute the mark

Hi Antonin, thanks for the library.

Right now I'm trying to get information from a very simple service:

package com.hm;

import com.codahale.metrics.annotation.Metered;
import io.astefanutti.metrics.aspectj.Metrics;

@Metrics(registry = "registryName")
public class Service {

    @Metered(name = "sayHi")
    public void sayHi(){
        System.out.println("Hi!");
    }
}

This service is called by a simple Main class with a ConsoleReport:

package com.hm;

import java.util.concurrent.TimeUnit;
import com.codahale.metrics.ConsoleReporter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.SharedMetricRegistries;

public class Main {

    public static void main(String[] args) {
        MetricRegistry registry = SharedMetricRegistries.getOrCreate("registryName");

        ConsoleReporter reporter = ConsoleReporter.forRegistry(registry)
                .convertRatesTo(TimeUnit.SECONDS)
                .convertDurationsTo(TimeUnit.MILLISECONDS).build();
        reporter.start(1, TimeUnit.SECONDS);

        new Service().sayHi();
    }
}

and the output looks like the aspect never calls the mark() method from the Meter class:

Hi!
17/06/15 14:20:01 ==============================================================


17/06/15 14:20:02 ==============================================================


17/06/15 14:20:03 ==============================================================


17/06/15 14:20:04 ==============================================================


17/06/15 14:20:05 ==============================================================

Is there something else that we have to do in the @Metered annotation in order to call the mark() method?

This is my pom.xml right now:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.hm</groupId>
    <artifactId>metrics_example</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.6</maven.compiler.source>
        <maven.compiler.target>1.6</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>io.astefanutti.metrics.aspectj</groupId>
            <artifactId>metrics-aspectj</artifactId>
            <version>1.0.0</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>aspectj-maven-plugin</artifactId>
                <version>1.4</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>test-compile</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <source>${maven.compiler.source}</source>
                    <target>${maven.compiler.target}</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.3</version>
                <configuration>
                    <source>${maven.compiler.source}</source>
                    <target>${maven.compiler.target}</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Thanks again @astefanutti, great job with all those libraries.

Support histogram via return value and expression language

I have a use case for the histogram metric in my codebase and would love to see it supported via annotations like the rest. It presents some challenges in execution but there is a way it could work.

The annotation would be processed upon completion of the annotated method rather than around it as it would operate on the return value of the method.

Examples:

@Histogram(name = "return.integer")
public int returnsAnInteger() {
   return randomInt();
   // Histogram::update is called using return value if return type is int
}
// Since EL 2.2 arbitrary method calls are supported
// 'return' bean defined in the aspect as the return value of the annotated method
@Histogram(name = "return.customObject", value = "${return.size()}")
public List<String> returnsAList() {
   return listFetchedFromRemoteService();
   // Histogram::update is called using the value obtained via execution 
   // of the provided EL expression (in this case over the return value)
}
@Histogram(name = "return.instanceField", value = "${this.currentValue}")
public void returnsNothingButSideEffects() {
   this.setCurrentValue(someInt());
   // Histogram::update is called using the value obtained via execution
   // of the provided EL expression (in this case over the instance)
}

Any thoughts on this style? I'd be happy to implement this if it's ok to go in.

Introduction of default MetricRegistry

How about implementing some default MetricRegistry, so that the programmer was not forced to place @registry("") annotation everywhere along with @metrics? Only in case if he/she wants separate registries.
Or even combine these two annotations and specify registry name in @metrics annotation, like @metrics(registry = ""), also with a default value :)

ClassCastException: com.codahale.metrics.Timer cannot be cast to com.codahale.metrics.Meter

I'm getting the following bizarre exception at runtime:

java.lang.ClassCastException: com.codahale.metrics.Timer cannot be cast to com.codahale.metrics.Meter
! at org.stefanutti.metrics.aspectj.MeteredAspect.ajc$before$org_stefanutti_metrics_aspectj_MeteredAspect$1$ed31598a(MeteredAspect.aj:28) ~[license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at com.clarecontrols.licensing.cliq.service.impl.FusionAPIImpl.getProjectNameForUUID_aroundBody4(FusionAPIImpl.java:143) ~[license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at com.clarecontrols.licensing.cliq.service.impl.FusionAPIImpl$AjcClosure5.run(FusionAPIImpl.java:1) ~[license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at org.stefanutti.metrics.aspectj.TimedAspect.ajc$around$org_stefanutti_metrics_aspectj_TimedAspect$1$c493a7d9proceed(TimedAspect.aj:26) ~[license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at org.stefanutti.metrics.aspectj.TimedAspect.ajc$around$org_stefanutti_metrics_aspectj_TimedAspect$1$c493a7d9(TimedAspect.aj:31) ~[license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at com.clarecontrols.licensing.cliq.service.impl.FusionAPIImpl.getProjectNameForUUID(FusionAPIImpl.java:142) ~[license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at com.clarecontrols.licensing.cliq.api.LicenseInfoResource.getProjectName(LicenseInfoResource.java:146) [license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at com.clarecontrols.licensing.cliq.api.LicenseInfoResource.getLicenseInfoView(LicenseInfoResource.java:125) [license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_05]
! at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_05]
! at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_05]
! at java.lang.reflect.Method.invoke(Method.java:483) ~[na:1.8.0_05]
! at com.sun.jersey.spi.container.JavaMethodInvokerFactory$1.invoke(JavaMethodInvokerFactory.java:60) [license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at com.sun.jersey.server.impl.model.method.dispatch.AbstractResourceMethodDispatchProvider$TypeOutInvoker._dispatch(AbstractResourceMethodDispatchProvider.java:185) [license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at com.sun.jersey.server.impl.model.method.dispatch.ResourceJavaMethodDispatcher.dispatch(ResourceJavaMethodDispatcher.java:75) [license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at com.codahale.metrics.jersey.InstrumentedResourceMethodDispatchProvider$TimedRequestDispatcher.dispatch(InstrumentedResourceMethodDispatchProvider.java:30) [license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at com.codahale.metrics.jersey.InstrumentedResourceMethodDispatchProvider$MeteredRequestDispatcher.dispatch(InstrumentedResourceMethodDispatchProvider.java:49) [license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at io.dropwizard.jersey.guava.OptionalResourceMethodDispatchAdapter$OptionalRequestDispatcher.dispatch(OptionalResourceMethodDispatchAdapter.java:37) [license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at com.sun.jersey.server.impl.uri.rules.HttpMethodRule.accept(HttpMethodRule.java:302) [license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at com.sun.jersey.server.impl.uri.rules.RightHandPathRule.accept(RightHandPathRule.java:147) [license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at com.sun.jersey.server.impl.uri.rules.ResourceObjectRule.accept(ResourceObjectRule.java:100) [license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at com.sun.jersey.server.impl.uri.rules.RightHandPathRule.accept(RightHandPathRule.java:147) [license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at com.sun.jersey.server.impl.uri.rules.RootResourceClassesRule.accept(RootResourceClassesRule.java:84) [license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:1542) [license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:1473) [license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1419) [license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1409) [license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at com.sun.jersey.spi.container.servlet.WebComponent.service(WebComponent.java:409) [license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:540) [license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:715) [license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at javax.servlet.http.HttpServlet.service(HttpServlet.java:848) [license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at io.dropwizard.jetty.NonblockingServletHolder.handle(NonblockingServletHolder.java:49) [license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1515) [license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at org.eclipse.jetty.servlets.UserAgentFilter.doFilter(UserAgentFilter.java:83) [license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at org.eclipse.jetty.servlets.GzipFilter.doFilter(GzipFilter.java:348) [license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at io.dropwizard.jetty.BiDiGzipFilter.doFilter(BiDiGzipFilter.java:127) [license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1486) [license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at io.dropwizard.servlets.ThreadNameFilter.doFilter(ThreadNameFilter.java:29) [license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1486) [license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at io.dropwizard.jersey.filter.AllowedMethodsFilter.handle(AllowedMethodsFilter.java:44) [license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at io.dropwizard.jersey.filter.AllowedMethodsFilter.doFilter(AllowedMethodsFilter.java:39) [license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1486) [license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:519) [license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1097) [license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:448) [license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1031) [license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:136) [license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:97) [license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at com.codahale.metrics.jetty9.InstrumentedHandler.handle(InstrumentedHandler.java:175) [license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at io.dropwizard.jetty.RoutingHandler.handle(RoutingHandler.java:51) [license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:97) [license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at org.eclipse.jetty.server.handler.RequestLogHandler.handle(RequestLogHandler.java:92) [license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:97) [license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at org.eclipse.jetty.server.handler.StatisticsHandler.handle(StatisticsHandler.java:162) [license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:97) [license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at org.eclipse.jetty.server.Server.handle(Server.java:446) [license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:271) [license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:246) [license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at org.eclipse.jetty.io.AbstractConnection$ReadCallback.run(AbstractConnection.java:358) [license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:601) [license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:532) [license-server-1.0.1-SNAPSHOT.jar:1.0.1-SNAPSHOT]
! at java.lang.Thread.run(Thread.java:745) [na:1.8.0_05]

I'm trying to use this project to provide metrics around API calls that I am making inside of a Dropwizard environment (which handles metrics). Timers work but Meters fail with the above error.

I am building using Maven; below is a cut-down version of of the POM with the relevant parts:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <prerequisites>
        <maven>3.0.0</maven>
    </prerequisites>
    <groupId>com.clarecontrols.licensing.cliq</groupId>
    <artifactId>license-server</artifactId>
    <version>1.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>
    <name>CLIQ License Server</name>
    <!-- Elided for clarity -->
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <dropwizard.version>0.7.1</dropwizard.version>
        <mainClass>com.clarecontrols.licensing.cliq.LicenseServerApplication</mainClass>
        <mojo.java.target>1.7</mojo.java.target>
    </properties>
    <!-- Elided for clarity -->
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-shade-plugin</artifactId>
                <version>2.1</version>
                <configuration>
                    <createDependencyReducedPom>true</createDependencyReducedPom>
                    <transformers>
                        <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
                        <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                            <mainClass>${mainClass}</mainClass>
                        </transformer>
                    </transformers>
                    <!-- Exclude signed Manifests -->
                    <filters>
                        <filter>
                            <artifact>*:*</artifact>
                            <excludes>
                                <exclude>META-INF/*.SF</exclude>
                                <exclude>META-INF/*.DSA</exclude>
                                <exclude>META-INF/*.RSA</exclude>
                            </excludes>
                        </filter>
                    </filters>
                </configuration>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <artifactId>maven-jar-plugin</artifactId>
                <version>2.5</version>
                <configuration>
                    <archive>
                        <manifest>
                            <addClasspath>true</addClasspath>
                            <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
                            <mainClass>${mainClass}</mainClass>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.0</version>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-source-plugin</artifactId>
                <version>2.2.1</version>
                <executions>
                    <execution>
                        <id>attach-sources</id>
                        <goals>
                            <goal>jar</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <artifactId>maven-javadoc-plugin</artifactId>
                <version>2.9</version>
                <executions>
                    <execution>
                        <id>attach-javadocs</id>
                        <goals>
                            <goal>jar</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <artifactId>maven-deploy-plugin</artifactId>
                <version>2.7</version>
                <dependencies>
                    <dependency>
                        <groupId>org.apache.maven.wagon</groupId>
                        <artifactId>wagon-ssh</artifactId>
                        <version>2.6</version>
                    </dependency>
                </dependencies>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>aspectj-maven-plugin</artifactId>
                <version>1.7</version>
                <configuration>
                    <aspectLibraries>
                        <aspectLibrary>
                            <groupId>org.stefanutti.metrics.aspectj</groupId>
                            <artifactId>metrics-aspectj</artifactId>
                        </aspectLibrary>
                    </aspectLibraries>
                    <complianceLevel>1.7</complianceLevel>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <!-- Elided for clarity -->
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>io.dropwizard</groupId>
            <artifactId>dropwizard-assets</artifactId>
            <version>${dropwizard.version}</version>
        </dependency>
        <dependency>
            <groupId>io.dropwizard</groupId>
            <artifactId>dropwizard-auth</artifactId>
            <version>${dropwizard.version}</version>
        </dependency>
        <dependency>
            <groupId>io.dropwizard</groupId>
            <artifactId>dropwizard-core</artifactId>
            <version>${dropwizard.version}</version>
            <exclusions>
                <!-- Provided by org.stefanutti.metrics.aspectj:metrics-aspectj -->
                <exclusion>
                    <groupId>org.glassfish.web</groupId>
                    <artifactId>javax.el</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>com.codahale.metrics</groupId>
                    <artifactId>metrics-core</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>io.dropwizard</groupId>
            <artifactId>dropwizard-jdbi</artifactId>
            <version>${dropwizard.version}</version>
        </dependency>
        <dependency>
            <groupId>io.dropwizard</groupId>
            <artifactId>dropwizard-migrations</artifactId>
            <version>${dropwizard.version}</version>
        </dependency>
        <dependency>
            <groupId>io.dropwizard</groupId>
            <artifactId>dropwizard-views-mustache</artifactId>
            <version>${dropwizard.version}</version>
        </dependency>
        <dependency>
            <groupId>org.stefanutti.metrics.aspectj</groupId>
            <artifactId>metrics-aspectj</artifactId>
            <version>1.0.0-rc.3</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.8.2</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish</groupId>
            <artifactId>javax.el</artifactId>
            <version>3.0.0</version>
        </dependency>
        <!-- Elided for clarity -->
    </dependencies>
</project>

The service implementation that I'm instrumenting:

package com.clarecontrols.licensing.cliq.service.impl;

import org.stefanutti.metrics.aspectj.Metrics;
// Elided for clarity
import com.clarecontrols.licensing.cliq.service.FusionAPI;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.annotation.Metered;
import com.codahale.metrics.annotation.Timed;

/**
 * FusionAPIImpl
 */
@Metrics(registry = "${this.registry}")
public class FusionAPIImpl implements FusionAPI {

    private final MetricRegistry registry;
    // Elided for clarity

    /**
     * Constructor.
     * @param registry the metrics registry
     * // Elided for clarity
     */
    public FusionAPIImpl(final MetricRegistry registry // Elided for clarity) {
        this.registry = registry;
    }

    /**
     * Required for EL to find the metrics registry property for the @Metrics annotation.
     * @return the metric registry
     */
    public MetricRegistry getRegistry() {
        return this.registry;
    }

    /**
     * {@inheritDoc}
     */
    @Timed(name = "updateLicenseType.timer")
    @Metered(name = "updateLicenseType.meter")
    @Override
    public void updateLicenseType(final String uuid, final LicenseType licenseType) {
        // Elided for clarity
    }

    /**
     * {@inheritDoc}
     */
    @Timed(name = "ping.timer")
    @Metered(name = "ping.meter")
    @Override
    public void ping() {
        // Elided for clarity
    }

    /**
     * {@inheritDoc}
     */
    @Timed(name = "getProjectNameForUUID.timer")
    @Metered(name = "getProjectNameForUUID.meter")
    @Override
    public String getProjectNameForUUID(final String uuid) {
        // Elided for clarity
        return null;
    }
}

Any ideas as to the cause of this? Thanks!

Gradle usage example would be "nice to have".

Maven is by far leading Gradle in terms of usage ( https://gradle.org/maven-vs-gradle ), so it is understandable why Gradle usage is not addressed here (yet).

That said, Gradle is used w/i a significant number of projects, including some that I contribute to, so I have created such an example:
https://github.com/paegun/metrics-aspectj-gradle-example

Please feel free to link to the example or bring such an example under your control so it can more easily remain up-to-date w/ metrics-aspectj .

A metric named "AnnotationService.setSize" already exists


@Metrics(registry = "${this.registry}")
public class AnnotationService {
    private Random random = new Random();
    private Set<String> set = new HashSet<String>();
    private MetricRegistry registry = null;

    public AnnotationService() {
        this(MetricRegisterFactory.getInstance());
    }

    public AnnotationService(MetricRegistry registry) {
        this.registry = registry;
        ConsoleReporter.forRegistry(this.registry).build().start(15, TimeUnit.SECONDS);
    }

    public MetricRegistry getRegistry() {
        return registry;
    }

    @Gauge
    public int setSize() {
        return set.size();
    }

    @CachedGauge(timeout = 10, timeoutUnit = TimeUnit.SECONDS)
    private int cachedSetSize() {
        return set.size();
    }

    private boolean add(String string) {
        return set.add(string);
    }

    private boolean remove(String string) {
        return set.remove(string);
    }

    public boolean gaugeService(String action, String string) {
        if ("add".equals(action)) {
            return add(string);
        } else if ("remove".equals(action)) {
            return remove(string);
        }
        return false;
    }

    //没有注册
    @Counted
    public void countedService() {
        try {
            Thread.sleep(random.nextInt(150));
        } catch (InterruptedException e) {
        }
    }

    @Metered
    public void meteredService() {
        try {
            Thread.sleep(random.nextInt(150));
        } catch (InterruptedException e) {
        }
    }

    @ExceptionMetered
    public void exceptionMeteredService() {
        if (!random.nextBoolean()) {
            throw new RuntimeException("runtime error");
        }
        try {
            Thread.sleep(random.nextInt(150));
        } catch (InterruptedException e) {
        }
    }

    @Timed
    public void timedService() {
        try {
            Thread.sleep(random.nextInt(150));
        } catch (InterruptedException e) {
        }
    }
}

Exception:
java.lang.IllegalArgumentException: A metric named AnnotationService.setSize already exists

I think it's a bug, because @metered not throw exception, it seems @Gauge will register when the method is called..

ps: report 3 metric:@Gauge @meterD @timed, but @ExceptionMetered do not report

if i remove @Gauge, report 3 metric:@meterD @timed @ExceptionMetered

please offer a proposal, thx

v1.0.0 Is Not In Maven Central

It looks like the last artifact to make it to Maven Central was RC5. Could you push the v1.0.0 release artifacts? Thanks!

@Timed method on base class throws NullPointerException when class is extended

Hi, metrics-aspectj developers

I have been using your library and found it very convenient to annotate a method to capture performance metrics. Recently I hit the wall when trying the extend a class that has annotated methods. Here's what I see:

@Metrics
public class Base {
    public void something_not_timed() {
        ...
   }
    @Timed(name="method1-ProcessTime")
    public void method1() {
       ...
    }
}

I have been using Base class in production without issue. Now I need to extend this class.

public class Derived extends Base {
    @Override
    public void something_not_timed() {
        ....
    }
}

As you can see, method1() is not overridden. But the problem is that whenever I use method1() in Derived class, I would get a NullPointerException in method1_aroundBody1$advice().

I'm not familiar with AspectJ so couldn't really figure out what I'm missing, any advice will be highly appreciated.

Thanks!

Regards,
Cary

EL Expression is not working with @Metrics annotation

In gradle example Instead of using registry name I chose to use EL Expression ${this.registry} and program started throwing exceptions. Below is the code:

`package com;

import java.util.Random;
import java.util.concurrent.TimeUnit;

import com.codahale.metrics.SharedMetricRegistries;

import com.codahale.metrics.ConsoleReporter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Meter;
import com.codahale.metrics.Timer;
import com.codahale.metrics.annotation.Timed;

import javax.el.ELProcessor;

import io.astefanutti.metrics.aspectj.Metrics;

// ~minimal App using metrics-aspectj
public class App {

//public static final MetricRegistry registry = SharedMetricRegistries.getOrCreate("reg");

public static void main(String[] args) throws InterruptedException {
    //MetricsRegistry registry = SharedMetricRegistries.getOrCreate("reg");
    //App.class.getClassLoader().loadClass("javax.el.ELProcessor");
    ITimedMethod timedMethod = new TimedMethod();

    for (int i = 0; i < 10; ++i) {
        timedMethod.randomTimeTask();
    }

    // await console reporter
    Thread.sleep(1010);
}

}

// this interface is not strictly necessary, but was added to make the toggling
// of explicit profiling easier.
//
// the class TimedMethodImplicit matches the ease of implementation for metrics
// that is documented and made possible by metrics-aspectj.
interface ITimedMethod {
public void randomTimeTask() throws InterruptedException;
}

class TimedMethod implements ITimedMethod {
// to use the same metric registry when explicitly (read: cross-cuttingly)
// profiling the App as well as when implicitly (read: aspect-oriented-ly)
// profiling the App, here for the explicit case, get or create
// (read: ~singleton) the metric registry using the same name as is used for
// the implicit case. FYI the default name for the implicit case is
// "metrics-registry".
private static final MetricRegistry metrics =
SharedMetricRegistries.getOrCreate("reg");

private ITimedMethod tmi;
private ITimedMethod tme;

public TimedMethod() {
    metricConsoleReporterStart();
    this.tmi = new TimedMethodImplicit(this.metrics);
    if (!AppConfig.isExplicitlyMeasuring) {
        this.tme = new TimedMethodNop();
    } else {
        this.tme = new TimedMethodExplicit(this.metrics);
    }
}

public void randomTimeTask() throws InterruptedException {
    tmi.randomTimeTask();
    tme.randomTimeTask();
}

private void metricConsoleReporterStart() {
    ConsoleReporter.forRegistry(this.metrics)
        .convertRatesTo(TimeUnit.SECONDS)
        .convertDurationsTo(TimeUnit.MILLISECONDS)
        .build()
        .start(1, TimeUnit.SECONDS);
}

}

@metrics(registry = "${ this.registry }")
class TimedMethodImplicit implements ITimedMethod {
private final Random rand = new Random();
private final MetricRegistry registry;

public TimedMethodImplicit(MetricRegistry reg) {
    this.registry = reg;
}

public MetricRegistry getRegistry(){return this.registry;}

@Timed(name = "randomTimeTask")
public void randomTimeTask() throws InterruptedException {
    Thread.sleep(rand.nextInt(100));
}

}
@metrics(registry = "reg")
class TimedMethodNop implements ITimedMethod {
@timed
public void randomTimeTask() throws InterruptedException { }
}

class TimedMethodExplicit implements ITimedMethod {
private final MetricRegistry metrics;
private final Timer timer;

private final Random rand = new Random();

public TimedMethodExplicit(MetricRegistry metrics) {
    this.metrics = metrics;
    this.timer = metrics.timer("request_times");
}

@Timed(name = "randomTimeTask")
public void randomTimeTask() throws InterruptedException {
    Timer.Context context = null;
    try {
        context = this.timer.time();
        Thread.sleep(rand.nextInt(100));
    } finally {
        if (context != null) {
            context.close();
        }
    }
}

}
`

NullPointerException on timed methods with several parameters

Method signatures are used for metrics resolution, but they are inconsistent.

  1. In MetricAspect#metricAnnotation(…) method signature is taken from 'method.toString()'.
  2. In TimedAspect it is taken from 'thisJoinPointStaticPart.getSignature().toLongString()'.

Those approaches produce slightly different results. The second separates method parameters with spaces and the first not.

This results in NullPointerException on timed methods with several parameters.

java.lang.NullPointerException
    org.stefanutti.metrics.aspectj.TimedAspect.ajc$inlineAccessMethod$org_stefanutti_metrics_aspectj_TimedAspect$org_stefanutti_metrics_aspectj_AnnotatedMetric$getMetric(TimedAspect.aj:1)
    org.stefanutti.metrics.aspectj.TimedAspect.ajc$around$org_stefanutti_metrics_aspectj_TimedAspect$1$c493a7d9(TimedAspect.aj:26)
    com.example.TestServlet.doGet(TestServlet.java:18)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:621)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
    org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.