/* * Copyright 2012-2015, the original author or authors. * * 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 com.flipkart.aesop.runtime.metrics; import java.util.HashMap; import java.util.Map; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import org.codehaus.jackson.map.ObjectMapper; import com.flipkart.aesop.runtime.relay.DefaultRelay; import com.flipkart.aesop.runtime.spring.web.RelayInfo; import com.linkedin.databus.core.monitoring.mbean.DbusEventsTotalStats; import com.linkedin.databus2.core.container.monitoring.mbean.DbusHttpTotalStats; /** * Periodically collects relay statistics for producers and consumers and serializes it into a JSON representation. * @author kartikbu * @created 09/06/14 */ public class MetricsCollector { /** Default refresh interval */ private static final int DEFAULT_REFRESH_INTERVAL = 1; /** relay instance */ private DefaultRelay relay; /** Scheduler for doing the encoding */ private ScheduledExecutorService scheduledExecutorService; /** Object mapper */ private ObjectMapper objectMapper = new ObjectMapper(); /** JSON encoded registry */ private String json = ""; /** refresh interval */ private int refreshInterval = DEFAULT_REFRESH_INTERVAL; /** Stats Collectors */ private DbusHttpTotalStats httpTotalStats; private DbusEventsTotalStats inboundTotalStats; private DbusEventsTotalStats outboundTotalStats; /** scn trackers */ private Map<String,Long> producerSCN = new HashMap<String,Long>(); private Map<String,Long> clientSCN = new HashMap<String,Long>(); /** * Constructor * @param relay HTTP relay instance */ public MetricsCollector(DefaultRelay relay) { this.relay = relay; // stats collectors this.httpTotalStats = relay.getHttpStatisticsCollector().getTotalStats(); this.inboundTotalStats = relay.getInboundEventStatisticsCollector().getTotalStats(); this.outboundTotalStats = relay.getOutboundEventStatisticsCollector().getTotalStats(); // schedule encoder thread scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(); scheduledExecutorService.scheduleAtFixedRate(new Encoder(this), 0, DEFAULT_REFRESH_INTERVAL, TimeUnit.SECONDS); } /** * Update SCN for a client * @param client Client as string * @param SCN SCN requested by client */ public void setClientSCN(String client, long SCN) { this.clientSCN.put(client,SCN); } /** * Update SCN generated by a producer * @param producer Producer as string * @param SCN SCN generated by producer */ public void setProducerSCN(String producer, long SCN) { this.producerSCN.put(producer,SCN); } /** * Get generated JSON * @return generated JSON */ public String getJson() { return json; } /** * Refresh interval, also used by controller * @return Refresh interval */ public int getRefreshInterval() { return refreshInterval; } /** * Encoder class which JSON encodes metrics data. */ public class Encoder implements Runnable { /** Collector instance */ private MetricsCollector collector; /** * Constructor * @param collector collector instance */ public Encoder(MetricsCollector collector) { this.collector = collector; } /** * Interface method implementation. */ @Override public void run() { Map<String,Object> map = new HashMap<String,Object>(); map.put("producer",this.collector.producerSCN); // we want stats of only connected clients as known to the Relay Map<String,Long> connectedClientSCN = new HashMap<String,Long>(); Map<String,Long> groupHostClient = new HashMap<String,Long>(); Long clientSCN = null; String clientHost = null; for (String client : relay.getPeers()) { clientSCN = this.collector.clientSCN.get(client); clientHost = RelayInfo.ClientInfo.parseHostFromClientName(client); // record minimum client per client Host String clientHostMinKey = clientHost + "-min"; if(groupHostClient.get(clientHostMinKey) == null || groupHostClient.get(clientHostMinKey) >= clientSCN) { groupHostClient.put(clientHostMinKey, clientSCN); } // record maximum client per client Host String clientHostMaxKey = clientHost + "-max"; if(groupHostClient.get(clientHostMaxKey) == null || groupHostClient.get(clientHostMaxKey) < clientSCN) { groupHostClient.put(clientHostMaxKey, clientSCN); } connectedClientSCN.put(client, clientSCN); } map.put("clientHost", groupHostClient); map.put("client", connectedClientSCN); map.put("http", this.collector.httpTotalStats); map.put("inbound", this.collector.inboundTotalStats); map.put("outbound", this.collector.outboundTotalStats); try { this.collector.json = this.collector.objectMapper.writeValueAsString(map); } catch (Exception e) { this.collector.json = this.mapException(e); } } /** * In case of exceptions while encoding this method is used to send an alternate JSON. * @param e Exception caught * @return String representation of exception. */ private String mapException(Exception e) { try { Map<String,String> map = new HashMap<String,String>(); map.put("status", "exception"); map.put("class", e.getClass().getName()); map.put("message", e.getMessage()); return objectMapper.writeValueAsString(map); } catch (Exception x) { return ""; } } } }