users@jersey.java.net

[Jersey] Re: Measuring execution time for each resource method separately

From: Stephane Bailliez <sbailliez_at_gmail.com>
Date: Wed, 28 Dec 2011 17:58:52 -0500

May or may not help, but try something like the code below

You can then apply it to individual resources
with: @ResourceFilters(TimingResourceFilter.class)

This can be generalized to everything, in the example below, the
StatisticsService is just a rough in memory service that keeps tracks of
metrics and perform statistical computation with streaming values (I used
commons-math), another implementation can easily be substituted, but that's
the idea. (note that the service could also be injected automatically too
by wrapping it into a SingletonTypeInjectableProvider when you declare it
as a singleton in your application)


/**
 * Timing filter on resources. It will monitor how long it takes to invoke
a particular resource and
 * store it on the statistics service.
 * <p/>
 * All resources will be stored in the 'rest' group.
 */
public class TimingResourceFilter implements ResourceFilter,
ContainerRequestFilter, ContainerResponseFilter {
    public static final String STATISTICS_GROUP = "rest";

    // filter() is actually called in 2 separate stages, so we need to
store the timing in a thread local

    private final static class TimerThreadLocal extends ThreadLocal<Long> {
        public long start() {
            long value = System.currentTimeMillis();
            this.set(value);
            return value;
        }

        public long stop() {
            return System.currentTimeMillis() - get();
        }

        @Override
        protected Long initialValue() {
            return System.currentTimeMillis();
        }
    }

    private final StatisticsService service;
    private final static TimerThreadLocal timer = new TimerThreadLocal();

    public TimingResourceFilter(@Context ServletContext hc) {
        this.service = (StatisticsService)
hc.getAttribute(StatisticsService.SERVICE_KEY);
    }

    public ContainerRequestFilter getRequestFilter() {
        return this;
    }

    public ContainerResponseFilter getResponseFilter() {
        return this;
    }

    public ContainerRequest filter(ContainerRequest request) {
        long now = timer.start();
        return request;
    }

    public ContainerResponse filter(ContainerRequest request,
ContainerResponse response) {
        long dt = timer.stop();
        service.add(STATISTICS_GROUP, request.getPath(true), dt); // store
metrics the way you want here
        return response;
    }
}


On Wed, Dec 28, 2011 at 3:18 PM, Marshall Pierce <marshall_at_mpierce.org>wrote:

> Hi,
> I'm trying to find a minimally invasive way of capturing various
> statistics about Jersey resource method invocation. It's pretty easy to
> hook into the inbound and outbound flow via com.sun.jersey.spi.container.*
> *ResourceFilterFactory and that should work fine for things like
> measuring the frequency of 5xx status codes. However, that doesn't offer an
> easy way to actually wrap an individual invocation the way
> javax.servlet.Filter does.
>
> This is what I'm trying to do conceptually (from
> http://metrics.codahale.com/**getting-started.html<http://metrics.codahale.com/getting-started.html>
> ):
>
>
> final TimerContext context = ...;
> try {
> // invoke resource method
> } finally {
> context.stop();
> }
>
>
> The goal is to be able to use a TimerContext (and other metrics) that are
> tied to each individual AbstractMethod so that I can see data for "GET
> /foo/{fooId}" without having to map "/foo/33" and "/foo/96" to the proper
> resource myself.
>
> It looks like one approach might be to combine a request-scoped
> TimerContext that gets set in a ContainerRequestFilter with a servlet
> Filter that actually measures the time and applies the measured invocation
> time to the timer. This feels messy, though.
>
> I can also do it via AOP, and that's not necessarily a bad way to go. I
> don't object to AOP; I'm just curious if I can do it cleanly without AOP.
>
> Any suggestions for a better way to do this?
>
> -Marshall
>