问题
I am trying to add metrics to a plain Java application using codahale metrics. I'd like to use the @Timed annotation, but it is unclear to me which MetricRegistry it uses, or how to tell it which MetricRegistry to use. The application is a plain Java 8 application, built with Maven 3, no Spring, no Hibernate.
I can not find any documentation on how to implement @Timed in the dropwizard documentation: https://dropwizard.github.io/metrics/3.1.0/manual/
I've added these dependencies:
<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>
When I use a programatic call to Timer, I can get reports because I know which MetricsRegistry is used:
static final MetricRegistry metrics = new MetricRegistry();
private void update() throws SQLException {
Timer.Context time = metrics.timer("domainobject.update").time();
try {
[...]
} finally {
time.stop();
}
}
But when I use the much more elegant @Timed annotation, I have no idea which registry is used, and therefore I can not create a reporter, which means I can not get the metrics reported (I'm not even sure if this actually does anything):
@Timed(name = "domainobject.update")
private void update() throws SQLException {
[...]
}
Please advise on how to make the @Timed and other Metrics annotations work in a regular Java application.
Additional info: The reason I am finding this strange is that I have added the Lombok framework and the @Slf4j annotations do work. I added Lombok as a dependency in the maven pom.xml:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.14.8</version>
</dependency>
And I can use the @Sl4fj class annotation to add a logger to the class without cluttering up the member variables:
@Slf4j
public class App {
public void logsome(){
log.info("Hello there");
}
}
So if that's possible by just adding a dependency, I reckon I am just missing a dependency or configuration to get the codahale @Timed annotation work, as described above.
(by the way, check out Lombok, it will make your life easier: http://projectlombok.org/ )
回答1:
Long story short, you cannot use @Timed
without some kind of AOP (be it Spring AOP or AspectJ).
A week or two ago, I also decided to add metrics to our project and chose AspectJ for this task (mostly because I used it in the past for similar purpose and because it allows for compile-time weaving while Spring allows only runtime via proxies).
You should be able to find all the necessary information and instructions here: https://github.com/astefanutti/metrics-aspectj.
As for Lombok, I guess they use built-in javac annotations processor:
Another point of contention is the implementation of both the code supporting IDE integration as well as the javac annotation processor. Both of these pieces of Project Lombok make use of non-public APIs to accomplish their sorcery. This means that there is a risk that Project Lombok will be broken with subsequent IDE or JDK releases.
回答2:
Using @Timed
doesn't actually require the use of AOP, as was previously claimed in the top-rated answer, if you're inside a container and using one of Dropwizard's instrumentation libraries. See the Jersey 2.x module for example, which you can see uses reflection (as do the others I looked at), if you read the source.
You can read up on all of these modules in the Dropwizard docs under the corresponding "Instrumenting ____" bullets.
I understand the OP was explicitly NOT working within such a container, but I wanted to offer this info, since many of us looking for this answer may be working on a modern web service that can register such resources in its runtime environment.
回答3:
Use the built-in MetricRegistry accessed from the bootstrap parameter in the initialize method of your application class.
@Override
public void initialize(final Bootstrap<Configuration> bootstrap) {
final JmxReporter reporter = JmxReporter.forRegistry(bootstrap.getMetricRegistry()).build();
reporter.start();
}
回答4:
As the other answer stated, you have to have something in the application to listen for your instantiated classes and check them for the @Timed annotation.
If you're using Guice, you could use: https://github.com/palominolabs/metrics-guice
回答5:
AOP is overkill and not appropriate for use of @timed, generally speaking.
The default metrics registry writes @timed metrics to a ConcurrentHashMap and does not attach any meaningful listeners.
DropWizard Bootstrap constructor:
/**
* 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>();
}
So you need to build/start/register the appropriate metric registry in order to see results.
Here I use JMX:
@Override
public void initialize(Bootstrap<PayloadStorageConfiguration> bootstrap) {
JmxReporter.forRegistry(bootstrap.getMetricRegistry()).build().start();
}
That's all you need to do.
Here's an example of the output (run jconsole against your Java application/server to view JMX results):
回答6:
In newer Dropwizard versions (I am using 0.9.2), you can access the default MetricRegistry
through the setup environment io.dropwizard.setup.Environment
. This default MetricRegistry
already has an InstrumentedResourceMethodApplicationListener
associated with it, which listens to all the metrics of your resources.
If you have registered a resource with the JerseyEnvironment
as under,
environment.jersey().register(resource);
you need only annotate your resource method (or class) with @Timed
, @Metered
or @ExceptionMetered
to register the respective metrics.
@POST
@Timed
public String show() {
return "yay";
}
You can assign a Reporter
(like an Slf4jReporter
or JmxReporter
) to the default MetricRegistry
as under
Slf4jReporter.forRegistry(environment.metrics()).build();
As a quick test to see if your metrics have actually been registered, you can make a GET
call to the URL http://localhost:8081/metrics
or the corresponding Admin Metrics URL in your test environment.
Some other versions require you to explicitly register an InstrumentedResourceMethodApplicationListener
as shown in this Doc
回答7:
You could also use stagemonitor-core for that. See documentation here and here. The advantage is that stagemonitor (which is free & open source btw) does not depend on any container-based AOP like Spring AOP or EJB interceptors. It uses bytecode manipulation via runtime attachment which means that you do not even have to add a -javaagent
flag to your application startup - a plain dependency is enough.
If you want to measure the execution time in a web application or in a remote EJB application, you don't even have to manually annotate your code. Also, stagemonitor offers preconfigured Grafana and Kibana dashboards.
Disclaimer: I'm one of the developers of stagemonitor
回答8:
this simple/right-to-the-point example https://karollotkowski.wordpress.com/2015/10/19/api-endpoint-in-one-minute-with-dropwizard/ just showed it only need the annotation
来源:https://stackoverflow.com/questions/28499621/codahale-metrics-using-timed-metrics-annotation-in-plain-java