Dropwizard Metric Annotations @Timed не работает

Я пытаюсь автоматически публиковать метрики в моем MetricRegistry, используя аннотации, такие как @Timed (http://metrics.dropwizard.io/3.1.0/apidocs/com/codahale/metrics/annotation/package-summary.html).

Это не работает. При поиске вопросов я нашел Codahale Metrics: используя аннотацию @Timed метрик в простой Java, где было упомянуто, что единственным способом для этого будет использование aspectj. Я добавил это в свой проект, но до сих пор не вижу своих показателей в моем MetricRegistry.

Это мой файл pom. Я добавил библиотеку библиотеки, которая загружается в com.codahale.metrics:metrics-annotation.

<dependency>
  <groupId>io.astefanutti.metrics.aspectj</groupId>
  <artifactId>metrics-aspectj</artifactId>
  <version>${metrics-aspectj.version}</version>
</dependency>
<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjrt</artifactId>
  <version>1.8.10</version>
</dependency>

  <plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>aspectj-maven-plugin</artifactId>
    <version>1.7</version>
    <configuration>
      <showWeaveInfo>true</showWeaveInfo>
      <source>1.8</source>
      <target>1.8</target>
      <complianceLevel>1.8</complianceLevel>
      <encoding>UTF-8</encoding>
      <verbose>true</verbose>
      <aspectLibraries>
        <aspectLibrary>
          <groupId>io.astefanutti.metrics.aspectj</groupId>
          <artifactId>metrics-aspectj</artifactId>
        </aspectLibrary>
      </aspectLibraries>
    </configuration>
    <executions>
      <execution>
        <phase>process-sources</phase>
        <goals>
          <goal>compile</goal>
          <goal>test-compile</goal>
        </goals>
      </execution>
    </executions>
  </plugin>

<dependency>
  <groupId>com.librato.metrics</groupId>
  <artifactId>metrics-librato</artifactId>
  <version>${metrics-librato.version}</version>
</dependency>

Вот как я пытаюсь использовать метрики

@Metrics(registry = "default") // this.metricRegistry is default
public class Foo {
    @Inject
    private MetricRegistry metricRegistry;
    ...

    @Metered(name = "meterName")
    public void bar() {
        Meter meter = metricRegistry.meter("manual");
        meter.mark();
        // this.metricRegistry does not contain "meterName" after the ConsoleReporter prints the metrics for "default"
        // this.metricRegistry contains "manual" after the ConsoleReporter prints the metrics for "default"
    }

Я вижу это в своих журналах, когда компилирую:

[INFO] Extending interface set for type 'Foo' (Foo.java) to include 'io.astefanutti.metrics.aspectj.Profiled' (MetricAspect.aj)
[INFO] Type 'Foo' (Foo.java) has intertyped field from 'io.astefanutti.metrics.aspectj.MetricAspect' (MetricAspect.aj:'java.util.Map<java.lang.String,io.astefanutti.metrics.aspectj.AnnotatedMetric<com.codahale.metrics.Gauge>> io.astefanutti.metrics.aspectj.Profiled.gauges')
[INFO] Type 'Foo' (Foo.java) has intertyped field from 'io.astefanutti.metrics.aspectj.MetricAspect' (MetricAspect.aj:'java.util.Map<java.lang.String,io.astefanutti.metrics.aspectj.AnnotatedMetric<com.codahale.metrics.Meter>> io.astefanutti.metrics.aspectj.Profiled.meters')
[INFO] Type 'Foo' (Foo.java) has intertyped field from 'io.astefanutti.metrics.aspectj.MetricAspect' (MetricAspect.aj:'java.util.Map<java.lang.String,io.astefanutti.metrics.aspectj.AnnotatedMetric<com.codahale.metrics.Timer>> io.astefanutti.metrics.aspectj.Profiled.timers')
[INFO] Join point 'staticinitialization(void Foo.<clinit>())' in Type 'Foo' (Foo.java:46) advised by after advice from 'io.astefanutti.metrics.aspectj.MetricStaticAspect' (metrics-aspectj-1.2.0.jar!MetricStaticAspect.class:41(from MetricStaticAspect.aj))
[INFO] Join point 'method-execution(void Foo.bar())' in Type 'Foo' (Foo.java:74) advised by around advice from 'io.astefanutti.metrics.aspectj.TimedAspect' (metrics-aspectj-1.2.0.jar!TimedAspect.class:26(from TimedAspect.aj))
[INFO] Join point 'method-execution(void Foo.bar())' in Type 'Foo' (Foo.java:74) advised by before advice from 'io.astefanutti.metrics.aspectj.MeteredAspect' (metrics-aspectj-1.2.0.jar!MeteredAspect.class:26(from MeteredAspect.aj))

Кажется, что показанные аннотированные показатели я работают правильно. Однако я также вижу это в моих журналах.

[WARNING] advice defined in io.astefanutti.metrics.aspectj.TimedStaticAspect has not been applied [Xlint:adviceDidNotMatch]
    /Users/x/.m2/repository/io/astefanutti/metrics/aspectj/metrics-aspectj/1.2.0/metrics-aspectj-1.2.0.jar!io/astefanutti/metrics/aspectj/TimedStaticAspect.class:26

[WARNING] advice defined in io.astefanutti.metrics.aspectj.ExceptionMeteredAspect has not been applied [Xlint:adviceDidNotMatch]
    /Users/x/.m2/repository/io/astefanutti/metrics/aspectj/metrics-aspectj/1.2.0/metrics-aspectj-1.2.0.jar!io/astefanutti/metrics/aspectj/ExceptionMeteredAspect.class:26

[WARNING] advice defined in io.astefanutti.metrics.aspectj.ExceptionMeteredStaticAspect has not been applied [Xlint:adviceDidNotMatch]
    /Users/x/.m2/repository/io/astefanutti/metrics/aspectj/metrics-aspectj/1.2.0/metrics-aspectj-1.2.0.jar!io/astefanutti/metrics/aspectj/ExceptionMeteredStaticAspect.class:26

[WARNING] advice defined in io.astefanutti.metrics.aspectj.MeteredStaticAspect has not been applied [Xlint:adviceDidNotMatch]
    /Users/x/.m2/repository/io/astefanutti/metrics/aspectj/metrics-aspectj/1.2.0/metrics-aspectj-1.2.0.jar!io/astefanutti/metrics/aspectj/MeteredStaticAspect.class:26

[WARNING] advice defined in io.astefanutti.metrics.aspectj.TimedAspect has not been applied [Xlint:adviceDidNotMatch]
    /Users/x/.m2/repository/io/astefanutti/metrics/aspectj/metrics-aspectj/1.2.0/metrics-aspectj-1.2.0.jar!io/astefanutti/metrics/aspectj/TimedAspect.class:26

[WARNING] advice defined in io.astefanutti.metrics.aspectj.TimedStaticAspect has not been applied [Xlint:adviceDidNotMatch]
    /Users/x/.m2/repository/io/astefanutti/metrics/aspectj/metrics-aspectj/1.2.0/metrics-aspectj-1.2.0.jar!io/astefanutti/metrics/aspectj/TimedStaticAspect.class:26

[WARNING] advice defined in io.astefanutti.metrics.aspectj.MetricAspect has not been applied [Xlint:adviceDidNotMatch]
    /Users/x/.m2/repository/io/astefanutti/metrics/aspectj/metrics-aspectj/1.2.0/metrics-aspectj-1.2.0.jar!io/astefanutti/metrics/aspectj/MetricAspect.class:45

[WARNING] advice defined in io.astefanutti.metrics.aspectj.ExceptionMeteredAspect has not been applied [Xlint:adviceDidNotMatch]
    /Users/x/.m2/repository/io/astefanutti/metrics/aspectj/metrics-aspectj/1.2.0/metrics-aspectj-1.2.0.jar!io/astefanutti/metrics/aspectj/ExceptionMeteredAspect.class:26

[WARNING] advice defined in io.astefanutti.metrics.aspectj.MeteredAspect has not been applied [Xlint:adviceDidNotMatch]
    /Users/x/.m2/repository/io/astefanutti/metrics/aspectj/metrics-aspectj/1.2.0/metrics-aspectj-1.2.0.jar!io/astefanutti/metrics/aspectj/MeteredAspect.class:26

[WARNING] advice defined in io.astefanutti.metrics.aspectj.ExceptionMeteredStaticAspect has not been applied [Xlint:adviceDidNotMatch]
    /Users/x/.m2/repository/io/astefanutti/metrics/aspectj/metrics-aspectj/1.2.0/metrics-aspectj-1.2.0.jar!io/astefanutti/metrics/aspectj/ExceptionMeteredStaticAspect.class:26

[WARNING] advice defined in io.astefanutti.metrics.aspectj.MetricStaticAspect has not been applied [Xlint:adviceDidNotMatch]
    /Users/x/.m2/repository/io/astefanutti/metrics/aspectj/metrics-aspectj/1.2.0/metrics-aspectj-1.2.0.jar!io/astefanutti/metrics/aspectj/MetricStaticAspect.class:41

[WARNING] advice defined in io.astefanutti.metrics.aspectj.MeteredStaticAspect has not been applied [Xlint:adviceDidNotMatch]
    /Users/x/.m2/repository/io/astefanutti/metrics/aspectj/metrics-aspectj/1.2.0/metrics-aspectj-1.2.0.jar!io/astefanutti/metrics/aspectj/MeteredStaticAspect.class:26

Вот моя установка приложения

@Override
public void run(AppConfiguration configuration, Environment environment) {

    ConsoleReporter reporter = ConsoleReporter.forRegistry(environment.metrics())
            .convertRatesTo(TimeUnit.SECONDS)
            .convertDurationsTo(TimeUnit.MILLISECONDS)
            .build();
    reporter.start(2, TimeUnit.MINUTES);
}

Ответ 1

Я просто попробовал это, и он работает хорошо. Вот моя вся настройка:

pom.xml:

<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>
    <groupId>com.mycompany.app</groupId>
    <artifactId>my-app</artifactId>
    <packaging>jar</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>my-app</name>
    <url>http://maven.apache.org</url>


    <dependencies>

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

    </dependencies>

    <build>
        <plugins>
            <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>
                    <complianceLevel>1.8</complianceLevel>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>2.4.1</version>
                <configuration>
                    <!-- get all project dependencies -->
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                    <!-- MainClass in mainfest make a executable jar -->
                    <archive>
                        <manifest>
                            <mainClass>com.mkyong.core.utils.App</mainClass>
                        </manifest>
                    </archive>

                </configuration>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <!-- bind to the packaging phase -->
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

Обратите внимание, что мне нужно добавить aspectj-maven-plugin. Я также добавляю плагин assembly для тестирования позже. Это действительно не обязательно, я просто не хотел идти на поиски зависимостей в своей локальной коробке.

Мой временной класс:

package com.mycompany.app;

import java.util.concurrent.ThreadLocalRandom;

import com.codahale.metrics.annotation.Timed;

import io.astefanutti.metrics.aspectj.Metrics;
@Metrics(registry = "someMetrics")
public class TimedClass {

    @Timed(name = "test")
    public void doSomething() {
        try {
            Thread.sleep(200L);
        } catch (InterruptedException e) {
        }
        System.out.println("Done");
    }

    @Timed(name = "test-random")
    public void doSomething2() {
        try {
            Thread.sleep(ThreadLocalRandom.current().nextLong(100L));
        } catch (InterruptedException e) {
        }
        System.out.println("Done");
    }
}

И приложение:

package com.mycompany.app;

import java.util.concurrent.TimeUnit;

import com.codahale.metrics.ConsoleReporter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.SharedMetricRegistries;
public class App {

    public static void main(String[] args) throws InterruptedException {
        MetricRegistry metrics =
                SharedMetricRegistries.getOrCreate("someMetrics");

        ConsoleReporter.forRegistry(metrics)
        .convertRatesTo(TimeUnit.SECONDS)
        .convertDurationsTo(TimeUnit.MILLISECONDS)
        .build()
        .start(1, TimeUnit.SECONDS);

        TimedClass c = new TimedClass();
        for(int i = 0; i < 10; i++) {
            c.doSomething();
            c.doSomething2();
        }
        c.doSomething();
        Thread.sleep(1010L);
    }
}

Что здесь происходит, так это то, что при компиляции плагина maven для аспект вы найдете аннотации, которые вы используете и применяете аспекты.

В вашем случае (и моем), например, это из источника метрики:

final aspect TimedAspect {

    pointcut timed(Profiled object) : execution(@Timed !static * (@Metrics Profiled+).*(..)) && this(object);

    Object around(Profiled object) : timed(object) {
        String methodSignature = ((MethodSignature) thisJoinPointStaticPart.getSignature()).getMethod().toString();
        Timer timer = object.timers.get(methodSignature).getMetric();
        Timer.Context context = timer.time();
        try {
            return proceed(object);
        } finally {
            context.stop();
        }
    }
}

Все вышеизложенное заключается в том, чтобы сказать:

Любой метод, который не статичен и имеет аннотацию @Timed, должен выполняться внутри метода "around".

Теперь вы можете скомпилировать и упаковать, выполнив следующие действия:

mvn clean install package

Это будет печатать кучу предупреждений о неприменимых аспектах, но это прекрасно (мы фактически не используем их).

Кроме того, теперь у вас есть жирная банка, содержащая ваше приложение, и вы можете выполнить ее, чтобы посмотреть, сработали ли мы:

java -cp target/my-app-1.0-SNAPSHOT-jar-with-dependencies.jar com.mycompany.app.App

Что будет печатать:

[email protected] ~/dev/repo/my-app $ java -cp target/my-app-1.0-SNAPSHOT-jar-with-dependencies.jar com.mycompany.app.App
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
Done
Done
Done
Done
Done
Done
Done
Done
04/01/18 15:10:42 ==============================================================

-- Timers ----------------------------------------------------------------------
com.mycompany.app.TimedClass.test
             count = 4
         mean rate = 4.11 calls/second
     1-minute rate = 0.00 calls/second
     5-minute rate = 0.00 calls/second
    15-minute rate = 0.00 calls/second
               min = 200.21 milliseconds
               max = 200.37 milliseconds
              mean = 200.29 milliseconds
            stddev = 0.06 milliseconds
            median = 200.25 milliseconds
              75% <= 200.32 milliseconds
              95% <= 200.37 milliseconds
              98% <= 200.37 milliseconds
              99% <= 200.37 milliseconds
            99.9% <= 200.37 milliseconds
com.mycompany.app.TimedClass.test-random
             count = 4
         mean rate = 4.10 calls/second
     1-minute rate = 0.00 calls/second
     5-minute rate = 0.00 calls/second
    15-minute rate = 0.00 calls/second
               min = 20.41 milliseconds
               max = 32.28 milliseconds
              mean = 26.07 milliseconds
            stddev = 4.56 milliseconds
            median = 28.26 milliseconds
              75% <= 32.28 milliseconds
              95% <= 32.28 milliseconds
              98% <= 32.28 milliseconds
              99% <= 32.28 milliseconds
            99.9% <= 32.28 milliseconds


Done
Done
Done
Done
Done
Done
Done
04/01/18 15:10:43 ==============================================================

-- Timers ----------------------------------------------------------------------
com.mycompany.app.TimedClass.test
             count = 8
         mean rate = 4.06 calls/second
     1-minute rate = 0.00 calls/second
     5-minute rate = 0.00 calls/second
    15-minute rate = 0.00 calls/second
               min = 200.16 milliseconds
               max = 200.37 milliseconds
              mean = 200.25 milliseconds
            stddev = 0.06 milliseconds
            median = 200.25 milliseconds
              75% <= 200.26 milliseconds
              95% <= 200.37 milliseconds
              98% <= 200.37 milliseconds
              99% <= 200.37 milliseconds
            99.9% <= 200.37 milliseconds
com.mycompany.app.TimedClass.test-random
             count = 7
         mean rate = 3.55 calls/second
     1-minute rate = 0.00 calls/second
     5-minute rate = 0.00 calls/second
    15-minute rate = 0.00 calls/second
               min = 20.41 milliseconds
               max = 97.20 milliseconds
              mean = 43.68 milliseconds
            stddev = 28.17 milliseconds
            median = 28.26 milliseconds
              75% <= 76.30 milliseconds
              95% <= 97.20 milliseconds
              98% <= 97.20 milliseconds
              99% <= 97.20 milliseconds
            99.9% <= 97.20 milliseconds


Done
Done
Done
Done
Done
Done
04/01/18 15:10:44 ==============================================================

-- Timers ----------------------------------------------------------------------
com.mycompany.app.TimedClass.test
             count = 11
         mean rate = 3.70 calls/second
     1-minute rate = 0.00 calls/second
     5-minute rate = 0.00 calls/second
    15-minute rate = 0.00 calls/second
               min = 200.13 milliseconds
               max = 200.37 milliseconds
              mean = 200.23 milliseconds
            stddev = 0.07 milliseconds
            median = 200.21 milliseconds
              75% <= 200.26 milliseconds
              95% <= 200.37 milliseconds
              98% <= 200.37 milliseconds
              99% <= 200.37 milliseconds
            99.9% <= 200.37 milliseconds
com.mycompany.app.TimedClass.test-random
             count = 10
         mean rate = 3.37 calls/second
     1-minute rate = 0.00 calls/second
     5-minute rate = 0.00 calls/second
    15-minute rate = 0.00 calls/second
               min = 15.19 milliseconds
               max = 97.20 milliseconds
              mean = 48.90 milliseconds
            stddev = 30.99 milliseconds
            median = 32.28 milliseconds
              75% <= 76.30 milliseconds
              95% <= 97.20 milliseconds
              98% <= 97.20 milliseconds
              99% <= 97.20 milliseconds
            99.9% <= 97.20 milliseconds

Несколько точек:

Это не работает в Eclipse. Вам нужно будет установить расширение, чтобы заставить его работать (я не пробовал это), см. Здесь:

https://github.com/astefanutti/metrics-aspectj/blob/master/README.md

И для инструкций по использованию AJDT:

https://www.ibm.com/developerworks/library/j-ajdt/

Причина (неопределенность, потому что я не трачу слишком много времени на это), что аспекты применяются во время компиляции, а не во время выполнения. Eclipse использует свой собственный компилятор при запуске в среде IDE, поэтому он просто выполняет ваш код. Плагин maven внутри вашего pom файла, однако, является тем, что применяет аспекты при компиляции вашего кода.

Я надеюсь, что это поможет,

P.S. простить мое имя/упаковку - я использовал генератор maven по умолчанию

- Артур

Последующие действия:

Чтобы добавить метрики dropwizard, вы можете просто добавить их в общий, если хотите использовать вышеупомянутое решение. Например:

        MetricRegistry myCustomRegistry = new MetricRegistry();
        SharedMetricRegistries.add("my-metric-registry", myCustomRegistry);

В вашем случае это будет происходить от environment().metrics() или аналогичного.

Ответ 2

Проблема, с которой вы сталкиваетесь, заключается в том, что экземпляр MetricRegistry, используемый советом AspectJ, отличается от экземпляра, который вы используете для вашего теста ручного измерения, и вашего ConsoleReporter.

Так как ваш вопрос не указывает, откуда приходит экземпляр @Inject -ed MetricRegistry, я могу только предположить, что он тот же экземпляр, который вы используете с ConsoleReporter, но это , скорее всего, другой экземпляр из реестра "default", используемый советом AspectJ. Тот, который используется советом AspectJ, получен с помощью

SharedMetricRegistries.getOrCreate("default");

Чтобы решить вашу проблему, вы должны либо:

  • убедитесь, что экземпляр @Inject -ed MetricRegistry и тот, который был возвращен environment.metrics(), совпадает с SharedMetricRegistries.getOrCreate("default"), используя ваш предпочтительный путь внедрения инъекций зависимостей использования методов factory @Bean для Spring, @Produces для CDI).

  • используйте выражение EL в значении Foo class @Metrics registry registry, как это:

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

    предоставить геттер для указанного свойства MetricRegistry выше:

    public MetricRegistry getMetricRegistry() {
        return this.metricRegistry;
    }
    

    и убедитесь, что у вас есть требуемая библиотека EL в проекте:

    <dependency>
        <groupId>org.glassfish</groupId>
        <artifactId>javax.el</artifactId>
        <version>3.0.1-b09</version>
    </dependency>