package org.datadog.jmxfetch.reporter; import java.util.Arrays; import java.util.HashMap; import java.util.LinkedList; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.datadog.jmxfetch.App; import org.datadog.jmxfetch.Instance; import org.datadog.jmxfetch.JMXAttribute; public abstract class Reporter { private final static Logger LOGGER = Logger.getLogger(App.class.getName()); public static final String VALUE = "value"; private HashMap<String, Integer> serviceCheckCount; private HashMap<String, HashMap<String, HashMap<String, Object>>> ratesAggregator = new HashMap<String, HashMap<String, HashMap<String, Object>>>(); public Reporter() { this.serviceCheckCount = new HashMap<String, Integer>(); } String generateId(HashMap<String, Object> metric) { String key = (String) metric.get("alias"); for (String tag : (String[]) metric.get("tags")) { key += tag; } return key; } public void clearRatesAggregator(String instanceName) { ratesAggregator.put(instanceName, new HashMap<String, HashMap<String, Object>>()); } public void sendMetrics(LinkedList<HashMap<String, Object>> metrics, String instanceName) { HashMap<String, HashMap<String, Object>> instanceRatesAggregator; if (ratesAggregator.containsKey(instanceName)) { instanceRatesAggregator = ratesAggregator.get(instanceName); } else { instanceRatesAggregator = new HashMap<String, HashMap<String, Object>>(); } int loopCounter = App.getLoopCounter(); String sendingMessage = "Instance " + instanceName + " is sending " + metrics.size() + " metrics to the metrics reporter during collection #" + loopCounter; if (loopCounter <= 5 || loopCounter % 10 == 0) { LOGGER.info(sendingMessage); if (loopCounter == 5) { LOGGER.info("Next collections will be logged only every 10 collections."); } } else { LOGGER.debug(sendingMessage); } for (HashMap<String, Object> m : metrics) { // We need to edit metrics for legacy reasons (rename metrics, etc) HashMap<String, Object> metric = new HashMap<String, Object>(m); Double currentValue = (Double) metric.get(VALUE); if (currentValue.isNaN() || currentValue.isInfinite()) { continue; } String metricName = (String) metric.get("alias"); String metricType = (String) metric.get("metric_type"); String[] tags = Arrays.asList((String[]) metric.get("tags")).toArray(new String[0]); // StatsD doesn't support rate metrics so we need to have our own aggregator to compute rates if ("gauge".equals(metricType) || "histogram".equals(metricType)) { sendMetricPoint(metricType, metricName, currentValue, tags); } else { // The metric should be 'counter' String key = generateId(metric); if (!instanceRatesAggregator.containsKey(key)) { HashMap<String, Object> rateInfo = new HashMap<String, Object>(); rateInfo.put("ts", System.currentTimeMillis()); rateInfo.put(VALUE, currentValue); instanceRatesAggregator.put(key, rateInfo); continue; } long oldTs = (Long) instanceRatesAggregator.get(key).get("ts"); double oldValue = (Double) instanceRatesAggregator.get(key).get(VALUE); long now = System.currentTimeMillis(); double rate = 1000 * (currentValue - oldValue) / (now - oldTs); if (!Double.isNaN(rate) && !Double.isInfinite(rate)) { sendMetricPoint(metricType, metricName, rate, tags); } instanceRatesAggregator.get(key).put("ts", now); instanceRatesAggregator.get(key).put(VALUE, currentValue); } } ratesAggregator.put(instanceName, instanceRatesAggregator); } public void sendServiceCheck(String checkName, String status, String message, String[] tags){ this.incrementServiceCheckCount(checkName); String dataName = Reporter.formatServiceCheckPrefix(checkName); this.doSendServiceCheck(dataName, status, message, tags); } public void incrementServiceCheckCount(String checkName){ int scCount = this.getServiceCheckCount(checkName); this.getServiceCheckCountMap().put(checkName, new Integer(scCount+1)); } public int getServiceCheckCount(String checkName){ Integer scCount = this.serviceCheckCount.get(checkName); return (scCount == null) ? 0 : scCount.intValue(); } public void resetServiceCheckCount(String checkName){ this.serviceCheckCount.put(checkName, new Integer(0)); } protected HashMap<String, Integer> getServiceCheckCountMap(){ return this.serviceCheckCount; } public static String formatServiceCheckPrefix(String fullname){ String[] chunks = fullname.split("\\."); chunks[0] = chunks[0].replaceAll("[A-Z0-9:_\\-]", ""); return StringUtils.join(chunks, "."); } protected abstract void sendMetricPoint(String metricType, String metricName, double value, String[] tags); protected abstract void doSendServiceCheck(String checkName, String status, String message, String[] tags); public abstract void displayMetricReached(); public abstract void displayNonMatchingAttributeName(JMXAttribute jmxAttribute); public abstract void displayInstanceName(Instance instance); public abstract void displayMatchingAttributeName(JMXAttribute jmxAttribute, int rank, int limit); }