Java tutorial
package com.sparkred.mdex.metrics.publish; import java.io.*; import java.net.*; import java.util.*; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.apache.http.client.ClientProtocolException; import com.newrelic.metrics.publish.Agent; import com.newrelic.metrics.publish.util.Logger; import com.sparkred.mdex.metrics.MdexStatsEntry; import org.w3c.dom.*; import org.xml.sax.SAXException; /** * * Copyright 2014 Spark::red, LLC * * 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. This is the impl of the New Relic Agent class that is the base * of the Endeca monitor * * The basics are that it generates an http request to the configured host and * port and grabs the stats page located at /admin?op=stats. The result of this * "should" be well formed xml that is parsed into nodes, the results of which * are looped through and shoved into New Relic metrics that are pumped back to * the servers. * * * This initial impl reports on 4 root nodes on the status page : 1) * server_stats 2) resource_usage_stats 3) result_page_stats 4) navigation * * @author Gordon Cooke * */ public class EndecaMDEXAgent extends Agent { private static final Logger LOGGER = Logger.getLogger(EndecaMDEXAgent.class); private static final String METRIC_THRPT_10_SEC_AVG = "Throughput - 10 sec avg"; private static final String METRIC_THRPT_1_MIN_AVG = "Throughput - 1 min avg"; private static final String METRIC_THRPT_5_MIN_AVG = "Throughput - 5 min avg"; private static final String UNIT_REQ_SEC = "req/sec"; /** * As required by the Agent Class */ private static final String GUID = "com.sparkred.EndecaPlugin"; /** * As required by the Agent Class */ private static final String VERSION = "0.1"; /** * The host name to hit for the stats page */ private String mHost; /** * the port the mdex engine is running on */ private String mPort; /** * How to identify this particular agent uniquely */ private String mAgentName; /** * combination of all the various pieces making up the actual URL that gets * called */ private String mStatsUrl; /** * the path part of the URL */ private String mStatsURLPath = "/admin?op=stats"; /** * Allows for toggling the metrics off or on as needed */ private boolean mReportMetrics = true; /** * The list of metrics that we care about being pulled, this maps to the * elements that wrap the stats elements */ private static final String[] REPORTED_METRICS = new String[] { "server_stats", "resource_usage_stats", "result_page_stats", "navigation" }; /** * Constructor allowing for the set up of the host and port. There is no * other data required. * * @param pHost * @param pPort */ public EndecaMDEXAgent(String pHost, String pPort) { super(GUID, VERSION); this.mHost = pHost; this.mPort = pPort; this.mAgentName = "MDEX - " + mHost + ":" + mPort; this.mStatsUrl = "http://" + mHost + ":" + mPort + mStatsURLPath; } public EndecaMDEXAgent(String pHost, String pPort, String pName) { super(GUID, VERSION); this.mHost = pHost; this.mPort = pPort; this.mAgentName = pName; this.mStatsUrl = "http://" + mHost + ":" + mPort + mStatsURLPath; } /** * Implementation of the pollCycle as per the required API This impl makes * the call to the server, grabs and parses the stats page and runs thorugh * the list of reported metrics as per the REPORTED_METRICS array and * returns all of the relevant data * */ @Override public void pollCycle() { try { DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); Document doc = dBuilder.parse(new URL(mStatsUrl).openStream()); for (int i = 0; i < REPORTED_METRICS.length; i++) { List<MdexStatsEntry> statsList = fetchMDEXStatsByType(doc, REPORTED_METRICS[i]); reportMetrics(statsList); } // throughput stats have a different format reportThroughputMetrics(doc); } catch (ClientProtocolException e) { LOGGER.error(e, e.getMessage()); } catch (ConnectException ce) { LOGGER.error(ce, "Cannot connect to the provided URL, please validate your configs and make sure the host/port is accesible"); } catch (IOException e) { LOGGER.error(e, e.getMessage()); } catch (ParserConfigurationException e) { LOGGER.error(e, e.getMessage()); } catch (SAXException e) { LOGGER.error(e, e.getMessage()); } } /** * sample XML : <throughput units="req/sec" five_minute_avg="0.02" * one_minute_avg="0.06" ten_second_avg="0.07" /> * * * @param pDoc */ private void reportThroughputMetrics(Document pDoc) { NodeList thorughPut_stats = pDoc.getElementsByTagName("throughput"); Node node = thorughPut_stats.item(0); NamedNodeMap attr = node.getAttributes(); Double fiveMinuteAvg = Double.valueOf(attr.getNamedItem("five_minute_avg").getNodeValue()); Double oneMinuteAvg = Double.valueOf(attr.getNamedItem("one_minute_avg").getNodeValue()); Double tenSecondAvg = Double.valueOf(attr.getNamedItem("ten_second_avg").getNodeValue()); reportMetric(METRIC_THRPT_5_MIN_AVG, UNIT_REQ_SEC, fiveMinuteAvg); reportMetric(METRIC_THRPT_1_MIN_AVG, UNIT_REQ_SEC, oneMinuteAvg); reportMetric(METRIC_THRPT_10_SEC_AVG, UNIT_REQ_SEC, tenSecondAvg); } /** * Takes in the XML doc returned on the stats page and parses it out into a * list of MdexStatsEntry items * * @param pDoc * - the xml doc to pull data from * @param pRootNode * - the name of the node from which metrics should be pulled * @return - the list of items with the relevant metrics * @throws IOException * @throws ClientProtocolException */ private List<MdexStatsEntry> fetchMDEXStatsByType(Document pDoc, String pRootNode) throws IOException, ClientProtocolException { List<MdexStatsEntry> stats_entries = new ArrayList<MdexStatsEntry>(); NodeList server_stats = pDoc.getElementsByTagName(pRootNode); NodeList statsList = server_stats.item(0).getChildNodes(); for (int i = 0; i < statsList.getLength(); i++) { Node node = statsList.item(i); if (node.getNodeName().equals(MdexStatsEntry.STAT_NODE_NAME)) { NamedNodeMap attr = node.getAttributes(); MdexStatsEntry entry = new MdexStatsEntry( attr.getNamedItem(MdexStatsEntry.METRIC_NODE_NAME).getNodeValue(), attr.getNamedItem(MdexStatsEntry.METRIC_NODE_UNITS).getNodeValue(), attr.getNamedItem(MdexStatsEntry.METRIC_NODE_COUNT).getNodeValue(), attr.getNamedItem(MdexStatsEntry.METRIC_NODE_AVG).getNodeValue(), attr.getNamedItem(MdexStatsEntry.METRIC_NODE_STDDEV).getNodeValue(), attr.getNamedItem(MdexStatsEntry.METRIC_NODE_MIN).getNodeValue(), attr.getNamedItem(MdexStatsEntry.METRIC_NODE_MAX).getNodeValue(), attr.getNamedItem(MdexStatsEntry.METRIC_NODE_TOTAL).getNodeValue()); stats_entries.add(entry); } } return stats_entries; } /** * Send the metrics to New Relic. For every entry there are 5 metrics sent * the name is pulled from the entry name the units pulled from the units * the actual Metrics are Count, Total, Average, Min, Max * * @param pEntryList * - the list of MdexStatsEntrys to be looped through an report * metrics on */ private void reportMetrics(List<MdexStatsEntry> pEntryList) { for (Iterator<MdexStatsEntry> iterator = pEntryList.iterator(); iterator.hasNext();) { MdexStatsEntry mdexStatsEntry = (MdexStatsEntry) iterator.next(); reportMetric(mdexStatsEntry.getName() + " - Count", mdexStatsEntry.getUnits(), mdexStatsEntry.getCount()); reportMetric(mdexStatsEntry.getName() + " - Total", mdexStatsEntry.getUnits(), mdexStatsEntry.getTotal()); reportMetric(mdexStatsEntry.getName() + " - Average", mdexStatsEntry.getUnits(), mdexStatsEntry.getAverage()); reportMetric(mdexStatsEntry.getName() + " - Min", mdexStatsEntry.getUnits(), mdexStatsEntry.getMin()); reportMetric(mdexStatsEntry.getName() + " - Max", mdexStatsEntry.getUnits(), mdexStatsEntry.getMax()); } } @Override public String getAgentName() { return mAgentName; } public boolean isReportMetrics() { return mReportMetrics; } protected void setReportMetrics(boolean pReportMetrics) { mReportMetrics = pReportMetrics; } }