Metric.java

/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package org.esigate.extension.monitoring;

import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.ScheduledReporter;
import com.codahale.metrics.Slf4jReporter;
import org.apache.http.HttpStatus;
import org.apache.http.client.cache.CacheResponseStatus;
import org.apache.http.client.cache.HttpCacheContext;
import org.esigate.Driver;
import org.esigate.events.Event;
import org.esigate.events.EventDefinition;
import org.esigate.events.EventManager;
import org.esigate.events.IEventListener;
import org.esigate.events.impl.FetchEvent;
import org.esigate.events.impl.ProxyEvent;
import org.esigate.extension.Extension;
import org.esigate.util.Parameter;
import org.esigate.util.ParameterInteger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Properties;
import java.util.concurrent.TimeUnit;

/**
 * This extension will record proxy request, and backend request to generate statistics.
 * <p>
 * Result will be logged using SLF4J in INFO level every 60 seconds. Period can be configured in driver properties :
 * <p>
 * <code>metricPeriod=60</code>
 * <p>
 * <p>
 * Created by alexis on 20/03/14.
 */
public class Metric implements Extension, IEventListener {
    private static final Logger LOG = LoggerFactory.getLogger(Metric.class);
    private static final Parameter<Integer> PARAM_METRIC_PERIOD = new ParameterInteger("metricPeriod", 60);

    private MetricRegistry metric = new MetricRegistry();
    private Driver driver;

    @Override
    public void init(Driver d, Properties properties) {
        this.driver = d;
        LOG.debug("Initialize Metric");
        driver.getEventManager().register(EventManager.EVENT_PROXY_POST, this);
        driver.getEventManager().register(EventManager.EVENT_FETCH_POST, this);

        ScheduledReporter reporter =
                Slf4jReporter.forRegistry(this.metric).outputTo(LOG).convertRatesTo(TimeUnit.SECONDS)
                        .convertDurationsTo(TimeUnit.MILLISECONDS).build();

        reporter.start(PARAM_METRIC_PERIOD.getValue(properties), TimeUnit.SECONDS);
    }

    @Override
    public boolean event(EventDefinition id, Event event) {

        String timerName =
                MetricRegistry.name(this.getClass().getSimpleName(), driver.getConfiguration().getInstanceName(),
                        id.getId());

        if (EventManager.EVENT_PROXY_POST.equals(id)) {
            if (((ProxyEvent) event).getErrorPage() != null) {
                String statusCode =
                        String.valueOf(((ProxyEvent) event).getErrorPage().getHttpResponse().getStatusLine()
                                .getStatusCode());
                timerName = MetricRegistry.name(timerName, "error", statusCode);
            }
        } else if (EventManager.EVENT_FETCH_POST.equals(id)) {
            // Retrieve HTTP response status code and cache status
            FetchEvent e = (FetchEvent) event;
            int statusCode = e.getHttpResponse().getStatusLine().getStatusCode();
            CacheResponseStatus cacheResponseStatus =
                    (CacheResponseStatus) e.getHttpContext().getAttribute(HttpCacheContext.CACHE_RESPONSE_STATUS);

            // Adding status code when error
            if (statusCode >= HttpStatus.SC_BAD_REQUEST) {
                timerName = MetricRegistry.name(timerName, "error", String.valueOf(statusCode));
            }
            // Adding cache if not MISS
            if (cacheResponseStatus != null && !cacheResponseStatus.equals(CacheResponseStatus.CACHE_MISS)) {
                timerName = MetricRegistry.name(timerName, cacheResponseStatus.name().toLowerCase());
            }
        }

        metric.meter(timerName).mark();

        return true;
    }
}