public static Metric getOSMetric(String name) throws Exception { MetricPoller poller = new JmxMetricPoller(new LocalJmxConnector(), new ObjectName("java.lang:type=OperatingSystem"), MATCH_NONE); RegexMetricFilter filter = new RegexMetricFilter(null, Pattern.compile(name), false, false); List<Metric> metrics = poller.poll(filter); assertEquals(metrics.size(), 1); return metrics.get(0); } }
throws JMException, IOException { TagList tags = createTagList(name); MBeanInfo info = con.getMBeanInfo(name); MBeanAttributeInfo[] attrInfos = info.getAttributes(); List<Attribute> attributeList = safelyLoadAttributes(con, name, matchingNames); ((TabularData) obj).values().stream() .filter(key -> key instanceof CompositeData) .forEach(key -> addTabularMetrics(filter, metrics, tags, attrName, (CompositeData) key)); } else if (obj instanceof CompositeData) { addCompositeMetrics(filter, metrics, tags, attrName, (CompositeData) obj); } else { addMetric(metrics, attrName, tags, obj);
/** * Create a new metric object and add it to the list. */ private void addMetric( List<Metric> metrics, String name, TagList tags, Object value) { long now = System.currentTimeMillis(); if (onlyNumericMetrics) { value = asNumber(value); } if (value != null) { TagList newTags = counters.matches(MonitorConfig.builder(name).withTags(tags).build()) ? getTagListWithAdditionalTag(tags, DataSourceType.COUNTER) : getTagListWithAdditionalTag(tags, DataSourceType.GAUGE); Metric m = new Metric(name, newTags, now, value); metrics.add(m); } }
/** * There are issues loading some JMX attributes on some systems. This protects us from a * single bad attribute stopping us reading any metrics (or just a random sampling) out of * the system. */ private static List<Attribute> safelyLoadAttributes( MBeanServerConnection server, ObjectName objectName, List<String> matchingNames) { try { // first try batch loading all attributes as this is faster return batchLoadAttributes(server, objectName, matchingNames); } catch (Exception e) { // JBOSS ticket: https://issues.jboss.org/browse/AS7-4404 LOGGER.info("Error batch loading attributes for {} : {}", objectName, e.getMessage()); // some containers (jboss I am looking at you) fail the entire getAttributes request // if one is broken we can get the working attributes if we ask for them individually return individuallyLoadAttributes(server, objectName, matchingNames); } }
private void addCompositeMetrics(MetricFilter filter, List<Metric> metrics, TagList tags, String attrName, CompositeData obj) { Map<String, Object> values = new HashMap<>(); extractValues(null, values, obj); for (Map.Entry<String, Object> e : values.entrySet()) { final Tag compositeTag = Tags.newTag(COMPOSITE_PATH_KEY, e.getKey()); final TagList newTags = getTagListWithAdditionalTag(tags, compositeTag); if (filter.matches(MonitorConfig.builder(attrName).withTags(newTags).build())) { addMetric(metrics, attrName, newTags, e.getValue()); } } }
private void addTabularMetrics(MetricFilter filter, List<Metric> metrics, TagList tags, String attrName, CompositeData obj) { Map<String, Object> values = new HashMap<>(); // tabular composite data has a value called key and one called value values.put(obj.get("key").toString(), obj.get("value")); for (Map.Entry<String, Object> e : values.entrySet()) { final Tag compositeTag = Tags.newTag(COMPOSITE_PATH_KEY, e.getKey()); final TagList newTags = getTagListWithAdditionalTag(tags, compositeTag); if (filter.matches(MonitorConfig.builder(attrName).withTags(newTags).build())) { addMetric(metrics, attrName, newTags, e.getValue()); } } }
/** * Recursively extracts simple numeric values from composite data objects. * The map {@code values} will be populated with a path to the value as * the key and the simple object as the value. */ private void extractValues(String path, Map<String, Object> values, CompositeData obj) { for (String key : obj.getCompositeType().keySet()) { String newPath = (path == null) ? key : path + "." + key; Object value = obj.get(key); if (value instanceof CompositeData) { extractValues(newPath, values, (CompositeData) value); } else if (value != null) { values.put(newPath, value); } } }
/** * {@inheritDoc} */ public List<Metric> poll(MetricFilter filter, boolean reset) { List<Metric> metrics = new ArrayList<>(); try { MBeanServerConnection con = connector.getConnection(); for (ObjectName query : queries) { Set<ObjectName> names = con.queryNames(query, null); if (names.isEmpty()) { LOGGER.warn("no mbeans matched query: {}", query); } else { for (ObjectName name : names) { try { getMetrics(con, filter, metrics, name); } catch (Exception e) { LOGGER.warn("failed to get metrics for: " + name, e); } } } } } catch (Exception e) { LOGGER.warn("failed to collect jmx metrics.", e); } return metrics; }
private void addCompositeMetrics(MetricFilter filter, List<Metric> metrics, TagList tags, String attrName, CompositeData obj) { Map<String, Object> values = new HashMap<>(); extractValues(null, values, obj); for (Map.Entry<String, Object> e : values.entrySet()) { final Tag compositeTag = Tags.newTag(COMPOSITE_PATH_KEY, e.getKey()); final TagList newTags = getTagListWithAdditionalTag(tags, compositeTag); if (filter.matches(MonitorConfig.builder(attrName).withTags(newTags).build())) { addMetric(metrics, attrName, newTags, e.getValue()); } } }
private void addTabularMetrics(MetricFilter filter, List<Metric> metrics, TagList tags, String attrName, CompositeData obj) { Map<String, Object> values = new HashMap<>(); // tabular composite data has a value called key and one called value values.put(obj.get("key").toString(), obj.get("value")); for (Map.Entry<String, Object> e : values.entrySet()) { final Tag compositeTag = Tags.newTag(COMPOSITE_PATH_KEY, e.getKey()); final TagList newTags = getTagListWithAdditionalTag(tags, compositeTag); if (filter.matches(MonitorConfig.builder(attrName).withTags(newTags).build())) { addMetric(metrics, attrName, newTags, e.getValue()); } } }
/** * There are issues loading some JMX attributes on some systems. This protects us from a * single bad attribute stopping us reading any metrics (or just a random sampling) out of * the system. */ private static List<Attribute> safelyLoadAttributes( MBeanServerConnection server, ObjectName objectName, List<String> matchingNames) { try { // first try batch loading all attributes as this is faster return batchLoadAttributes(server, objectName, matchingNames); } catch (Exception e) { // JBOSS ticket: https://issues.jboss.org/browse/AS7-4404 LOGGER.info("Error batch loading attributes for {} : {}", objectName, e.getMessage()); // some containers (jboss I am looking at you) fail the entire getAttributes request // if one is broken we can get the working attributes if we ask for them individually return individuallyLoadAttributes(server, objectName, matchingNames); } }
/** * Recursively extracts simple numeric values from composite data objects. * The map {@code values} will be populated with a path to the value as * the key and the simple object as the value. */ private void extractValues(String path, Map<String, Object> values, CompositeData obj) { for (String key : obj.getCompositeType().keySet()) { String newPath = (path == null) ? key : path + "." + key; Object value = obj.get(key); if (value instanceof CompositeData) { extractValues(newPath, values, (CompositeData) value); } else if (value != null) { values.put(newPath, value); } } }
/** * {@inheritDoc} */ public List<Metric> poll(MetricFilter filter, boolean reset) { List<Metric> metrics = new ArrayList<>(); try { MBeanServerConnection con = connector.getConnection(); for (ObjectName query : queries) { Set<ObjectName> names = con.queryNames(query, null); if (names.isEmpty()) { LOGGER.warn("no mbeans matched query: {}", query); } else { for (ObjectName name : names) { try { getMetrics(con, filter, metrics, name); } catch (Exception e) { LOGGER.warn("failed to get metrics for: " + name, e); } } } } } catch (Exception e) { LOGGER.warn("failed to collect jmx metrics.", e); } return metrics; }
throws JMException, IOException { TagList tags = createTagList(name); MBeanInfo info = con.getMBeanInfo(name); MBeanAttributeInfo[] attrInfos = info.getAttributes(); List<Attribute> attributeList = safelyLoadAttributes(con, name, matchingNames); ((TabularData) obj).values().stream() .filter(key -> key instanceof CompositeData) .forEach(key -> addTabularMetrics(filter, metrics, tags, attrName, (CompositeData) key)); } else if (obj instanceof CompositeData) { addCompositeMetrics(filter, metrics, tags, attrName, (CompositeData) obj); } else { addMetric(metrics, attrName, tags, obj);
@Test public void testDefaultTags() throws Exception { MetricPoller poller = new JmxMetricPoller( new LocalJmxConnector(), Collections.singletonList(new ObjectName("java.lang:type=OperatingSystem")), MATCH_ALL, true, Collections.singletonList(Tags.newTag("HostName", "localhost"))); List<Metric> metrics = poller.poll(MATCH_ALL); for (Metric m : metrics) { Map<String, String> tags = m.getConfig().getTags().asMap(); assertEquals(tags.get("HostName"), "localhost"); } }
/** * Create a new metric object and add it to the list. */ private void addMetric( List<Metric> metrics, String name, TagList tags, Object value) { long now = System.currentTimeMillis(); if (onlyNumericMetrics) { value = asNumber(value); } if (value != null) { TagList newTags = counters.matches(MonitorConfig.builder(name).withTags(tags).build()) ? getTagListWithAdditionalTag(tags, DataSourceType.COUNTER) : getTagListWithAdditionalTag(tags, DataSourceType.GAUGE); Metric m = new Metric(name, newTags, now, value); metrics.add(m); } }
@Test public void testNonNumericMetrics() throws Exception { MapMXBean mapMXBean = new MapMXBean(); try { MetricPoller poller = new JmxMetricPoller( new LocalJmxConnector(), Collections.singletonList(new ObjectName("com.netflix.servo.test:*")), MATCH_ALL, false, null); List<Metric> metrics = poller.poll(config -> config.getName().equals("StringValue")); assertEquals(metrics.size(), 1); assertEquals(metrics.get(0).getValue(), "AStringResult"); } finally { mapMXBean.destroy(); } } }
@Test public void testCounterFilter() throws Exception { MetricPoller poller = new JmxMetricPoller( new LocalJmxConnector(), new ObjectName("java.lang:type=OperatingSystem"), MATCH_ALL); boolean found = false; List<Metric> metrics = poller.poll(MATCH_ALL); for (Metric m : metrics) { if ("AvailableProcessors".equals(m.getConfig().getName())) { found = true; Map<String, String> tags = m.getConfig().getTags().asMap(); assertEquals(tags.get("JmxDomain"), "java.lang"); assertEquals(tags.get("Jmx.type"), "OperatingSystem"); assertEquals(tags.get("ClassName"), "com.netflix.servo.publish.JmxMetricPoller"); assertEquals(tags.get(DataSourceType.KEY), "COUNTER"); } } assertTrue(found); }
@Test public void testBasic() throws Exception { MetricPoller poller = new JmxMetricPoller( new LocalJmxConnector(), new ObjectName("java.lang:type=OperatingSystem"), MATCH_NONE); boolean found = false; List<Metric> metrics = poller.poll(MATCH_ALL); for (Metric m : metrics) { if ("AvailableProcessors".equals(m.getConfig().getName())) { found = true; Map<String, String> tags = m.getConfig().getTags().asMap(); assertEquals(tags.get("JmxDomain"), "java.lang"); assertEquals(tags.get("Jmx.type"), "OperatingSystem"); assertEquals(tags.get("ClassName"), "com.netflix.servo.publish.JmxMetricPoller"); assertEquals(tags.get(DataSourceType.KEY), "GAUGE"); } } assertTrue(found); }
/** * Tabular JMX values are very useful for cases where we want the same behavior as CompositePath but we * don't know up front what the values are going to be. */ @Test public void testTabularData() throws Exception { MapMXBean mapMXBean = new MapMXBean(); try { MetricPoller poller = new JmxMetricPoller( new LocalJmxConnector(), new ObjectName("com.netflix.servo.test:*"), MATCH_ALL); List<Metric> metrics = poller.poll(config -> config.getName().equals("Count")); assertEquals(metrics.size(), 2); Map<String, Integer> values = new HashMap<>(); for (Metric m : metrics) { values.put(m.getConfig().getTags().getTag("JmxCompositePath").getValue(), (Integer) m.getValue()); } assertEquals(values.get("Entry1"), (Integer) 111); assertEquals(values.get("Entry2"), (Integer) 222); } finally { mapMXBean.destroy(); } }