/* * Copyright © 2014 Cask Data, Inc. * * 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 co.cask.cdap.common.metrics; import co.cask.cdap.api.metrics.MetricsCollectionService; import co.cask.cdap.api.metrics.MetricsContext; import co.cask.cdap.common.conf.Constants; import co.cask.cdap.proto.Id; import co.cask.http.AbstractHandlerHook; import co.cask.http.HandlerInfo; import co.cask.http.HttpResponder; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.collect.ImmutableMap; import org.jboss.netty.handler.codec.http.HttpRequest; import org.jboss.netty.handler.codec.http.HttpResponseStatus; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Map; import java.util.concurrent.TimeUnit; /** * Records gateway requests/response metrics. */ public class MetricsReporterHook extends AbstractHandlerHook { private static final Logger LOG = LoggerFactory.getLogger(MetricsReporterHook.class); private final MetricsCollectionService metricsCollectionService; private final String serviceName; private final LoadingCache<Map<String, String>, MetricsContext> collectorCache; public MetricsReporterHook(final MetricsCollectionService metricsCollectionService, String serviceName) { this.metricsCollectionService = metricsCollectionService; this.serviceName = serviceName; if (metricsCollectionService != null) { this.collectorCache = CacheBuilder.newBuilder() .expireAfterAccess(1, TimeUnit.HOURS) .build(new CacheLoader<Map<String, String>, MetricsContext>() { @Override public MetricsContext load(Map<String, String> key) throws Exception { return metricsCollectionService.getContext(key); } }); } else { collectorCache = null; } } @Override public boolean preCall(HttpRequest request, HttpResponder responder, HandlerInfo handlerInfo) { if (metricsCollectionService != null) { try { MetricsContext collector = collectorCache.get(createContext(handlerInfo)); collector.increment("request.received", 1); } catch (Throwable e) { LOG.error("Got exception while getting collector", e); } } return true; } @Override public void postCall(HttpRequest request, HttpResponseStatus status, HandlerInfo handlerInfo) { if (metricsCollectionService != null) { try { MetricsContext collector = collectorCache.get(createContext(handlerInfo)); String name; int code = status.getCode(); if (code < 100) { name = "unknown"; } else if (code < 200) { name = "information"; } else if (code < 300) { name = "successful"; } else if (code < 400) { name = "redirect"; } else if (code < 500) { name = "client-error"; } else if (code < 600) { name = "server-error"; } else { name = "unknown"; } // todo: report metrics broken down by status collector.increment("response." + name, 1/*, "status:" + code*/); } catch (Throwable e) { LOG.error("Got exception while getting collector", e); } } } private Map<String, String> createContext(HandlerInfo handlerInfo) { // todo: really inefficient to call this on the intense data flow path return ImmutableMap.of( Constants.Metrics.Tag.NAMESPACE, Id.Namespace.SYSTEM.getId(), Constants.Metrics.Tag.COMPONENT, serviceName, Constants.Metrics.Tag.HANDLER, getSimpleName(handlerInfo.getHandlerName()), Constants.Metrics.Tag.METHOD, handlerInfo.getMethodName()); } private String getSimpleName(String className) { int ind = className.lastIndexOf('.'); return className.substring(ind + 1); } }