Codahale Metrics:在纯 Java 中使用 @Timed 指标注释

Posted

技术标签:

【中文标题】Codahale Metrics:在纯 Java 中使用 @Timed 指标注释【英文标题】:Codahale Metrics: using @Timed metrics annotation in plain Java 【发布时间】:2015-04-14 11:54:35 【问题描述】:

我正在尝试使用 codahale 指标将指标添加到普通 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 
    [...]

请告知如何使 @Timed 和其他 Metrics 注释在常规 Java 应用程序中工作。

附加信息: 我觉得这很奇怪的原因是我添加了 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 注释正常工作,如上所述。

(顺便看看龙目岛,它会让你的生活更轻松:http://projectlombok.org/)

【问题讨论】:

我认为如果没有 Spring AOP 或手动设置 AspectJ 来编织你的类,你会很不走运。这种事情只适用于 AOP,所以你需要一些东西来在你的类中提供它。例如,Spring AOP 使用 JDK Proxies、CGLIB 或 AspectJ 来实现这一点 那么 AspectJ 会是正确的选择吗?我该怎么做呢? 更有趣的是:为什么@Slf4j 可以工作,而@Timed 不行?我没有明确使用 AspectJ 来让 Slf4j 工作。 你是什么意思@ Slf4j?我不熟悉使用 slf4j 的任何基于注释的 AOP 查看附加信息。 【参考方案1】:

长话短说,如果没有某种 AOP(无论是 Spring AOP 还是 AspectJ),你就不能使用 @Timed

一两个星期前,我还决定向我们的项目添加指标,并为此任务选择了 AspectJ(主要是因为我过去将它用于类似目的,并且因为它允许编译时编织,而 Spring 只允许运行时通过代理)。

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

至于 Lombok,我猜他们使用内置 javac 注释处理器:

另一个争论点是支持 IDE 集成的代码以及 javac 注释处理器的实现。 Project Lombok 的这两个部分都使用非公共 API 来完成他们的魔法。这意味着 Project Lombok 存在被后续 IDE 或 JDK 版本破坏的风险。

【讨论】:

这似乎确实是让@Timed 工作的方式,谢谢,但是现在 Lombok 和 AspectJ 在我的构建周期中陷入了争论。 AspectJ 抱怨 @Slf4j 注释。我必须弄清楚如何让他们玩得很好。 Lombok 和 AspectJ 似乎并不总是玩得很好:***.com/questions/25903686/… 是时候做出艰难的决定了。我试图删除 Lombok 以支持 @Timed 注释,但是 AspectJ 的东西让我很难过,而且调试起来非常困难和难以理解,我已经放弃了。它使项目变得更加复杂,并产生了比它正在解决的问题更大的问题。我确实将您的答案标记为“已回答”,因为它答案,这对我的小项目(和我的小大脑)来说太麻烦了。现在手动编码对 Codahale Timers 的调用。谢谢! 你也可以在cdi环境中使用metrics-cdi 我会尝试查看 InstrumentedResourceMethodApplicationListener,因为这就是 metrics-jersey2 的实现方式。它支持反射而不是 AOP。它不适用于在方法调用之前和之后没有执行代码的机制的情况(就像在 Jersey 中的请求处理流程一样)。【参考方案2】:

正如另一个答案所述,您必须在应用程序中有一些东西来监听您的实例化类并检查它们是否有 @Timed 注释。

如果您使用的是 Guice,您可以使用:https://github.com/palominolabs/metrics-guice

【讨论】:

我们在生产系统中使用了它。这很有用。【参考方案3】:

使用从应用程序类的初始化方法中的引导参数访问的内置 MetricRegistry。

@Override
public void initialize(final Bootstrap<Configuration> bootstrap) 
    final JmxReporter reporter = JmxReporter.forRegistry(bootstrap.getMetricRegistry()).build();
    reporter.start();

【讨论】:

【参考方案4】:

AOP 太过分了,通常不适合使用@timed 说话。

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

DropWizard Bootstrap 构造函数:

/**
 * 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();

这就是你需要做的。

这是一个输出示例(针对您的 Java 应用程序/服务器运行 jconsole 以查看 JMX 结果):

【讨论】:

【参考方案5】:

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

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

免责声明:我是 stagemonitor 的开发者之一

【讨论】:

【参考方案6】:

使用 @Timed 实际上并不需要使用 AOP,正如之前在***答案中所声称的那样,如果您在容器内并使用 Dropwizard 的仪器库之一。例如,请参阅 Jersey 2.x 模块,如果您阅读 source,您可以看到它使用反射(就像我查看的其他模块一样)。

您可以在the Dropwizard docs 中相应的“Instrumenting ____”项目符号下阅读所有这些模块。

我了解 OP 明确不在这样的容器中工作,但我想提供此信息,因为我们中的许多人正在寻找此答案,可能正在使用可以在其运行时环境中注册此类资源的现代 Web 服务。

【讨论】:

【参考方案7】:

在较新的 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 或相应的管理指标 URL 进行 GET 调用。

其他一些版本要求您显式注册InstrumentedResourceMethodApplicationListener,如in this Doc所示

【讨论】:

【参考方案8】:

这个简单/直接的例子https://karollotkowski.wordpress.com/2015/10/19/api-endpoint-in-one-minute-with-dropwizard/ 只是表明它只需要注释

【讨论】:

以上是关于Codahale Metrics:在纯 Java 中使用 @Timed 指标注释的主要内容,如果未能解决你的问题,请参考以下文章

Java程序监控---Metrics

codahale 的“指标”的其他开源替代品? [关闭]

DropWizard Metrics 米与计时器

MonitorService-监控服务类1

微服务之springcloud技术栈

ruby https://github.com/codahale/bcrypt-ruby/issues/142