Monday, August 5, 2013

Adding Coda Hale Metrics to Tomcat

I spent a couple of frustrating hours trying to get the Coda Hale Metrics (also known as 'Yammer Metrics') working in a simple Tomcat instance. The documentation isn't great, so I found a couple of steps that were missing. (Note: It has been a few years since I've had to mess with the web.xml, so that might be why it took so long. Spring MVC and annotations means less .xml hell!)

First you need to define a Listener to your web.xml:

<listener>
 <listener-class>com.company.urltesting.MetricsListener</listener-class>
 </listener>
The logic in the Listener is pretty straightforward:

import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.servlets.MetricsServlet;

public class MetricsListener extends MetricsServlet.ContextListener {
    public static final MetricRegistry REGISTRY = new MetricRegistry();

    @Override
    protected MetricRegistry getMetricRegistry() {
        return REGISTRY;
    }
}


The 'magic' here is the Coda Hale logic will call your listener to setup the Registry the first time. It isn't clear here but it is adding a specifically named Registry to the SessionContext (more about this in a minute).
Next setup the Servlet filter to be able to query the Metrics via an HTTP browser:


<servlet>
        <servlet-name>CodahaleMetrics</servlet-name>
        <servlet-class>com.codahale.metrics.servlets.MetricsServlet</servlet-class>
        <init-param>
            <param-name>metrics-uri</param-name>
            <param-value>/metrics</param-value>
        </init-param>
        <init-param>
            <param-name>ping-uri</param-name>
            <param-value>/ping</param-value>
        </init-param>
        <init-param>
            <param-name>healthcheck-uri</param-name>
            <param-value>/health</param-value>
        </init-param>
        <init-param>
            <param-name>threads-uri</param-name>
            <param-value>/threads</param-value>
        </init-param>
    </servlet>

    <servlet-mapping>
        <servlet-name>CodahaleMetrics</servlet-name>
        <url-pattern>/CodahaleMetrics/*</url-pattern>
    </servlet-mapping>





Finally, to create/use a Metric you need to know how to get them yourself from the Session Context.

Here is the most basic use:
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Timer;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class Encrypted extends HttpServlet {
   // public static final MetricRegistry metrics = new MetricRegistry();

    public void service(HttpServletRequest request,
                        HttpServletResponse response) {

        ServletContext servletContext = getServletContext();
        MetricRegistry metrics = (MetricRegistry) servletContext.getAttribute("com.codahale.metrics.servlets.MetricsServlet.registry");
        Timer tempTimer = metrics.timer(MetricRegistry.name(Encrypted.class, "Encrypted"));
        final Timer.Context context = tempTimer.time();

        //  do your work
        context.stop();
    }
}



Note the 'magic' string in the getAttribute call on the Servlet Context. The Metrics library adds this during startup if you've provided the Listener we defined in the first step.

Finally, hit the reporting URL to see your Metric values:

http://localhost:8080/CodahaleMetrics/metrics

{"version":"3.0.0","gauges":{},"counters":{},"histograms":{},"meters":{},"timers":{"com.company.urltesting.Encrypted.Encrypted":{"count":1,"max":0.21837355500000002,"mean":0.21837355500000002,"min":0.21837355500000002,
"p50":0.21837355500000002,"p75":0.21837355500000002,"p95":0.21837355500000002,
"p98":0.21837355500000002,"p99":0.21837355500000002,"p999":0.21837355500000002,
"stddev":0.0,"m15_rate":0.0,"m1_rate":0.0,"m5_rate":0.0,"mean_rate":0.2579625731812282,
"duration_units":"seconds","rate_units":"calls/second"}}}

4 comments:

Unknown said...

Hi Chris.
Sorry if I bother you. I am a novice to Storm. I am learning how to write trident in recent days. I read you great code herehttps://github.com/ChrisCurtin/trident-TransactionalFixedBatchSpout/blob/master/src/com/curtinhome/trident/spout/FixedBatchPartitionId.java

It implements a transactionalFixedBatchSpout for storm 0.9.0. And I wonder is there a version of you code for the storm 0.8.2? Or if you could concisely tell me how to re-write it for storm version 0.8.2? Thank you very much!

Unknown said...

my email is churylin@gmail.com thanks again. ^ ^

Thiago Negri said...

Thanks. You helped me! :-)

Unknown said...

hi ,

I replicated above example but i'm getting response as follows :

{"version":"3.0.0","gauges":{},"counters":{},"histograms":{},"meters":{},"timers":


Not able to get metric terms like mean rate and percentile etc.
can you please tell any thing to add!!!


Thanks in advance