Codahale指标:在普通Java中使用@Timed指标注释

44

我正在尝试使用codahale metrics为普通的Java应用程序添加度量标准。我想使用@Timed注释,但是我不清楚它使用哪个MetricRegistry,或者如何告诉它使用哪个MetricRegistry。该应用程序是一个纯Java 8应用程序,使用Maven 3构建,没有Spring,没有Hibernate。

我找不到任何有关在dropwizard文档中实现@Timed的文档:https://dropwizard.github.io/metrics/3.1.0/manual/

我已经添加了这些依赖项:

<dependency>
  <groupId>io.dropwizard.metrics</groupId>
  <artifactId>metrics-core</artifactId>
  <version>3.1.0</version>
</dependency>
<dependency>
  <groupId>com.codahale.metrics</groupId>
  <artifactId>metrics-annotation</artifactId>
  <version>3.0.2</version>
</dependency>

当我使用编程调用Timer时,我可以得到报告,因为我知道使用了哪个MetricsRegistry:
static final MetricRegistry metrics = new MetricRegistry();
private void update() throws SQLException {
  Timer.Context time = metrics.timer("domainobject.update").time();
  try {
    [...]
  } finally {
    time.stop();
  }
}

但是当我使用更加优雅的@Timed注解时,我不知道使用哪个注册表,因此我无法创建报告器,这意味着我无法获取报告的指标(我甚至不确定这是否实际上会有任何作用):
@Timed(name = "domainobject.update")
private void update() throws SQLException {
    [...]
}

请指导如何在普通Java应用程序中使用@Timed和其他指标注释。 附加信息:我觉得奇怪的原因是我已经添加了Lombok框架,@Slf4j注释确实起作用了。我在Maven的pom.xml文件中添加了Lombok依赖项。
<dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
  <version>1.14.8</version>
</dependency>

我可以使用@Sl4fj类注释来向类中添加日志记录器,而不会弄乱成员变量:

@Slf4j
public class App {
  public void logsome(){
    log.info("Hello there");
  }
}

所以如果只需添加一个依赖项就能实现,我想我可能只是缺少某个依赖项或配置才能让codahale的@Timed注释正常工作,如上所述。

(顺便说一下,看看Lombok,它会让你的生活更轻松: http://projectlombok.org/ )


我认为如果没有Spring AOP或手动设置AspectJ来编织您的类,您将会很倒霉。这种事情只能通过AOP来实现,因此您需要将其提供到您的类中。例如,Spring AOP使用JDK代理、CGLIB或AspectJ来实现这一点。 - Alex
那么使用AspectJ是正确的选择吗?我该如何开始呢? - Rolf
更有趣的是:为什么@ Slf4j可以工作,而@ Timed却不能?我并没有明确地使用AspectJ来使Slf4j工作。 - Rolf
你说的 @ Slf4j 是什么意思?我不熟悉任何基于注解的 AOP 与 slf4j。 - Alex
您可以通过在应用程序类中包含以下内容来修复:@Override public void initialize(Bootstrap<PayloadStorageConfiguration> bootstrap) {JmxReporter.forRegistry(bootstrap.getMetricRegistry()).build().start();} - Robert Christian
显示剩余2条评论
8个回答

19

简而言之,若要使用@Timed注解,就必须使用某种类型的AOP(无论是Spring AOP还是AspectJ)。

一两周前,我决定给我们的项目添加指标,并选择了AspectJ来完成此任务(主要是因为我在过去使用它进行类似的工作,并且因为它允许在编译时进行织入,而Spring只允许运行时通过代理实现)。

您应该能够在此处找到所有必要的信息和说明:https://github.com/astefanutti/metrics-aspectj

至于Lombok,我想他们使用内置的javac注解处理器:

另一个争议点是支持IDE集成的代码实现以及javac注解处理器的实现。 Project Lombok的这两个组件都使用非公共API来完成其工作。这意味着Project Lombok将存在与随后的IDE或JDK版本发布不兼容的风险。


这似乎是让@ Timed正常工作的方法,谢谢。但现在Lombok和AspectJ在我的构建周期中发生了争执。AspectJ抱怨@ Slf4j注释。我必须想办法让它们友好相处。 - Rolf
似乎Lombok和AspectJ并不总是相容的:https://dev59.com/hF8e5IYBdhLWcg3wRYwk - Rolf
是时候做出艰难的决定了。我试图删除Lombok,转而使用@Timed注释,但AspectJ的东西让我非常困扰,而且很难理解和调试,所以我放弃了。它使项目变得更加复杂,并且创造了比它解决的问题更大的问题。我将您的答案标记为“已回答”,因为它确实是答案,只是对于我的小项目(和我的小脑袋)来说太麻烦了。现在手动编写对Codahale计时器的调用。谢谢! - Rolf
您也可以在CDI环境中使用metrics-cdi - James Newman
我建议您仔细查看InstrumentedResourceMethodApplicationListener,因为它就是metrics-jersey2的实现方式。它倾向于使用反射而非AOP。这种方式并不适合在没有执行方法调用前后代码机制的情况下使用(例如在Jersey中处理请求流程)。 - Moshe Bixenshpaner

12

使用@Timed并不需要使用AOP,这与前面排名最高的答案所声称的不同,如果你正在容器内并且使用Dropwizard的一个仪表库之一,那么就可以使用它。例如,可以查看Jersey 2.x模块,如果阅读源代码,可以看到它使用了反射(其他我查看过的模块也是如此)。

您可以在Dropwizard文档中的相应"Instrumenting ____"项目下了解所有这些模块。

我知道原帖作者明确表示没有在这样的容器内工作,但我想提供这些信息,因为我们中的许多人可能正在开发可以在其运行时环境中注册此类资源的现代Web服务。


5

我们在生产系统中使用了这个。这非常有用。 - Manu Manjunath

5
使用内置的MetricRegistry,在应用程序类的initialize方法中从启动参数访问它。
@Override
public void initialize(final Bootstrap<Configuration> bootstrap) {
    final JmxReporter reporter = JmxReporter.forRegistry(bootstrap.getMetricRegistry()).build();
    reporter.start();
}

3

通常来说,AOP过于繁琐,不适合用于@timed。

默认的指标注册表将@timed指标写入ConcurrentHashMap,并不附加任何有意义的监听器。

DropWizard引导构造函数:

/**
 * Creates a new {@link Bootstrap} for the given application.
 * @param application a Dropwizard {@link Application}
 */
public Bootstrap(Application<T> application) {
    this.application = application;
    this.objectMapper = Jackson.newObjectMapper();
    this.bundles = Lists.newArrayList();
    this.configuredBundles = Lists.newArrayList();
    this.commands = Lists.newArrayList();
    this.validatorFactory = Validators.newValidatorFactory();


    // returns new ConcurrentHashMap<String, Metric>(); 
    this.metricRegistry = new MetricRegistry(); 


    this.configurationSourceProvider = new FileConfigurationSourceProvider();
    this.classLoader = Thread.currentThread().getContextClassLoader();
    this.configurationFactoryFactory = new DefaultConfigurationFactoryFactory<T>();
}

因此,您需要构建/启动/注册适当的指标注册表才能查看结果。

在这里,我使用JMX:

@Override
public void initialize(Bootstrap<PayloadStorageConfiguration> bootstrap) {
    JmxReporter.forRegistry(bootstrap.getMetricRegistry()).build().start();
}

这就是你需要做的全部。

以下是输出示例(运行jconsole来查看Java应用程序/服务器的JMX结果):

输入图像描述


1

0
在更新的Dropwizard版本中(我使用的是0.9.2),您可以通过设置环境io.dropwizard.setup.Environment访问默认的MetricRegistry。这个默认的MetricRegistry已经与一个InstrumentedResourceMethodApplicationListener相关联,它监听您资源的所有指标。
如果您已经将资源注册到JerseyEnvironment中,则可以像下面这样:
environment.jersey().register(resource);

你只需要在资源方法(或类)上注释@Timed@Metered@ExceptionMetered,就可以注册相应的指标。

@POST
@Timed
public String show() {
    return "yay";
}

您可以将一个Reporter(例如Slf4jReporterJmxReporter)分配给默认的MetricRegistry,如下所示:

Slf4jReporter.forRegistry(environment.metrics()).build();

作为一个快速测试,以查看您的指标是否已经注册,您可以对URL http://localhost:8081/metrics进行GET调用或在您的测试环境中使用对应的管理指标URL。
有一些其他的版本需要您显式注册InstrumentedResourceMethodApplicationListener,如此文档中所示。

0

你也可以使用stagemonitor-core来实现。请参阅此处此处的文档。优点是stagemonitor(顺便说一下,它是免费且开源的)不依赖于任何基于容器的AOP,如Spring AOP或EJB拦截器。它通过运行时附加使用字节码操作,这意味着您甚至不必在应用程序启动时添加-javaagent标志-仅需一个简单的依赖项即可。

如果您想要在Web应用程序或远程EJB应用程序中测量执行时间,则甚至不必手动注释代码。此外,stagemonitor提供了预配置的Grafana和Kibana仪表板。

免责声明:我是stagemonitor的开发人员之一。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接