/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.flink.metrics.jmx;
import org.apache.flink.configuration.ConfigConstants;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.configuration.MetricOptions;
import org.apache.flink.metrics.Gauge;
import org.apache.flink.metrics.util.TestMeter;
import org.apache.flink.metrics.reporter.MetricReporter;
import org.apache.flink.runtime.metrics.MetricRegistry;
import org.apache.flink.runtime.metrics.MetricRegistryConfiguration;
import org.apache.flink.runtime.metrics.groups.FrontMetricGroup;
import org.apache.flink.runtime.metrics.groups.TaskManagerMetricGroup;
import org.apache.flink.runtime.metrics.util.TestReporter;
import org.apache.flink.runtime.metrics.util.TestingHistogram;
import org.apache.flink.util.TestLogger;
import org.junit.Test;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanInfo;
import javax.management.MBeanServer;
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import java.lang.management.ManagementFactory;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import static org.apache.flink.metrics.jmx.JMXReporter.JMX_DOMAIN_PREFIX;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class JMXReporterTest extends TestLogger {
@Test
public void testReplaceInvalidChars() {
assertEquals("", JMXReporter.replaceInvalidChars(""));
assertEquals("abc", JMXReporter.replaceInvalidChars("abc"));
assertEquals("abc", JMXReporter.replaceInvalidChars("abc\""));
assertEquals("abc", JMXReporter.replaceInvalidChars("\"abc"));
assertEquals("abc", JMXReporter.replaceInvalidChars("\"abc\""));
assertEquals("abc", JMXReporter.replaceInvalidChars("\"a\"b\"c\""));
assertEquals("", JMXReporter.replaceInvalidChars("\"\"\"\""));
assertEquals("____", JMXReporter.replaceInvalidChars(" "));
assertEquals("ab_-(c)-", JMXReporter.replaceInvalidChars("\"ab ;(c)'"));
assertEquals("a_b_c", JMXReporter.replaceInvalidChars("a b c"));
assertEquals("a_b_c_", JMXReporter.replaceInvalidChars("a b c "));
assertEquals("a-b-c-", JMXReporter.replaceInvalidChars("a;b'c*"));
assertEquals("a------b------c", JMXReporter.replaceInvalidChars("a,=;:?'b,=;:?'c"));
}
/**
* Verifies that the JMXReporter properly generates the JMX table.
*/
@Test
public void testGenerateTable() {
Map<String, String> vars = new HashMap<>();
vars.put("key0", "value0");
vars.put("key1", "value1");
vars.put("\"key2,=;:?'", "\"value2 (test),=;:?'");
Hashtable<String, String> jmxTable = JMXReporter.generateJmxTable(vars);
assertEquals("value0", jmxTable.get("key0"));
assertEquals("value1", jmxTable.get("key1"));
assertEquals("value2_(test)------", jmxTable.get("key2------"));
}
/**
* Verifies that multiple JMXReporters can be started on the same machine and register metrics at the MBeanServer.
*
* @throws Exception if the attribute/mbean could not be found or the test is broken
*/
@Test
public void testPortConflictHandling() throws Exception {
Configuration cfg = new Configuration();
cfg.setString(MetricOptions.REPORTERS_LIST, "test1,test2");
cfg.setString(ConfigConstants.METRICS_REPORTER_PREFIX + "test1." + ConfigConstants.METRICS_REPORTER_CLASS_SUFFIX, JMXReporter.class.getName());
cfg.setString(ConfigConstants.METRICS_REPORTER_PREFIX + "test1.port", "9020-9035");
cfg.setString(ConfigConstants.METRICS_REPORTER_PREFIX + "test2." + ConfigConstants.METRICS_REPORTER_CLASS_SUFFIX, JMXReporter.class.getName());
cfg.setString(ConfigConstants.METRICS_REPORTER_PREFIX + "test2.port", "9020-9035");
MetricRegistry reg = new MetricRegistry(MetricRegistryConfiguration.fromConfiguration(cfg));
TaskManagerMetricGroup mg = new TaskManagerMetricGroup(reg, "host", "tm");
List<MetricReporter> reporters = reg.getReporters();
assertTrue(reporters.size() == 2);
MetricReporter rep1 = reporters.get(0);
MetricReporter rep2 = reporters.get(1);
Gauge<Integer> g1 = new Gauge<Integer>() {
@Override
public Integer getValue() {
return 1;
}
};
Gauge<Integer> g2 = new Gauge<Integer>() {
@Override
public Integer getValue() {
return 2;
}
};
rep1.notifyOfAddedMetric(g1, "rep1", new FrontMetricGroup<>(0, new TaskManagerMetricGroup(reg, "host", "tm")));
rep2.notifyOfAddedMetric(g2, "rep2", new FrontMetricGroup<>(0, new TaskManagerMetricGroup(reg, "host", "tm")));
MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
ObjectName objectName1 = new ObjectName(JMX_DOMAIN_PREFIX + "taskmanager.rep1", JMXReporter.generateJmxTable(mg.getAllVariables()));
ObjectName objectName2 = new ObjectName(JMX_DOMAIN_PREFIX + "taskmanager.rep2", JMXReporter.generateJmxTable(mg.getAllVariables()));
assertEquals(1, mBeanServer.getAttribute(objectName1, "Value"));
assertEquals(2, mBeanServer.getAttribute(objectName2, "Value"));
rep1.notifyOfRemovedMetric(g1, "rep1", null);
rep1.notifyOfRemovedMetric(g2, "rep2", null);
mg.close();
reg.shutdown();
}
/**
* Verifies that we can connect to multiple JMXReporters running on the same machine.
*
* @throws Exception
*/
@Test
public void testJMXAvailability() throws Exception {
Configuration cfg = new Configuration();
cfg.setString(ConfigConstants.METRICS_REPORTER_CLASS_SUFFIX, TestReporter.class.getName());
cfg.setString(MetricOptions.REPORTERS_LIST, "test1,test2");
cfg.setString(ConfigConstants.METRICS_REPORTER_PREFIX + "test1." + ConfigConstants.METRICS_REPORTER_CLASS_SUFFIX, JMXReporter.class.getName());
cfg.setString(ConfigConstants.METRICS_REPORTER_PREFIX + "test1.port", "9040-9055");
cfg.setString(ConfigConstants.METRICS_REPORTER_PREFIX + "test2." + ConfigConstants.METRICS_REPORTER_CLASS_SUFFIX, JMXReporter.class.getName());
cfg.setString(ConfigConstants.METRICS_REPORTER_PREFIX + "test2.port", "9040-9055");
MetricRegistry reg = new MetricRegistry(MetricRegistryConfiguration.fromConfiguration(cfg));
TaskManagerMetricGroup mg = new TaskManagerMetricGroup(reg, "host", "tm");
List<MetricReporter> reporters = reg.getReporters();
assertTrue(reporters.size() == 2);
MetricReporter rep1 = reporters.get(0);
MetricReporter rep2 = reporters.get(1);
Gauge<Integer> g1 = new Gauge<Integer>() {
@Override
public Integer getValue() {
return 1;
}
};
Gauge<Integer> g2 = new Gauge<Integer>() {
@Override
public Integer getValue() {
return 2;
}
};
rep1.notifyOfAddedMetric(g1, "rep1", new FrontMetricGroup<>(0, new TaskManagerMetricGroup(reg, "host", "tm")));
rep2.notifyOfAddedMetric(g2, "rep2", new FrontMetricGroup<>(1, new TaskManagerMetricGroup(reg, "host", "tm")));
ObjectName objectName1 = new ObjectName(JMX_DOMAIN_PREFIX + "taskmanager.rep1", JMXReporter.generateJmxTable(mg.getAllVariables()));
ObjectName objectName2 = new ObjectName(JMX_DOMAIN_PREFIX + "taskmanager.rep2", JMXReporter.generateJmxTable(mg.getAllVariables()));
JMXServiceURL url1 = new JMXServiceURL("service:jmx:rmi://localhost:" + ((JMXReporter)rep1).getPort() + "/jndi/rmi://localhost:" + ((JMXReporter)rep1).getPort() + "/jmxrmi");
JMXConnector jmxCon1 = JMXConnectorFactory.connect(url1);
MBeanServerConnection mCon1 = jmxCon1.getMBeanServerConnection();
assertEquals(1, mCon1.getAttribute(objectName1, "Value"));
assertEquals(2, mCon1.getAttribute(objectName2, "Value"));
jmxCon1.close();
JMXServiceURL url2 = new JMXServiceURL("service:jmx:rmi://localhost:" + ((JMXReporter)rep2).getPort() + "/jndi/rmi://localhost:" + ((JMXReporter)rep2).getPort() + "/jmxrmi");
JMXConnector jmxCon2 = JMXConnectorFactory.connect(url2);
MBeanServerConnection mCon2 = jmxCon2.getMBeanServerConnection();
assertEquals(1, mCon2.getAttribute(objectName1, "Value"));
assertEquals(2, mCon2.getAttribute(objectName2, "Value"));
rep1.notifyOfRemovedMetric(g1, "rep1", null);
rep1.notifyOfRemovedMetric(g2, "rep2", null);
jmxCon2.close();
rep1.close();
rep2.close();
mg.close();
reg.shutdown();
}
/**
* Tests that histograms are properly reported via the JMXReporter.
*/
@Test
public void testHistogramReporting() throws Exception {
MetricRegistry registry = null;
String histogramName = "histogram";
try {
Configuration config = new Configuration();
config.setString(MetricOptions.REPORTERS_LIST, "jmx_test");
config.setString(ConfigConstants.METRICS_REPORTER_PREFIX + "jmx_test." + ConfigConstants.METRICS_REPORTER_CLASS_SUFFIX, JMXReporter.class.getName());
registry = new MetricRegistry(MetricRegistryConfiguration.fromConfiguration(config));
TaskManagerMetricGroup metricGroup = new TaskManagerMetricGroup(registry, "localhost", "tmId");
TestingHistogram histogram = new TestingHistogram();
metricGroup.histogram(histogramName, histogram);
MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
ObjectName objectName = new ObjectName(JMX_DOMAIN_PREFIX + "taskmanager." + histogramName, JMXReporter.generateJmxTable(metricGroup.getAllVariables()));
MBeanInfo info = mBeanServer.getMBeanInfo(objectName);
MBeanAttributeInfo[] attributeInfos = info.getAttributes();
assertEquals(11, attributeInfos.length);
assertEquals(histogram.getCount(), mBeanServer.getAttribute(objectName, "Count"));
assertEquals(histogram.getStatistics().getMean(), mBeanServer.getAttribute(objectName, "Mean"));
assertEquals(histogram.getStatistics().getStdDev(), mBeanServer.getAttribute(objectName, "StdDev"));
assertEquals(histogram.getStatistics().getMax(), mBeanServer.getAttribute(objectName, "Max"));
assertEquals(histogram.getStatistics().getMin(), mBeanServer.getAttribute(objectName, "Min"));
assertEquals(histogram.getStatistics().getQuantile(0.5), mBeanServer.getAttribute(objectName, "Median"));
assertEquals(histogram.getStatistics().getQuantile(0.75), mBeanServer.getAttribute(objectName, "75thPercentile"));
assertEquals(histogram.getStatistics().getQuantile(0.95), mBeanServer.getAttribute(objectName, "95thPercentile"));
assertEquals(histogram.getStatistics().getQuantile(0.98), mBeanServer.getAttribute(objectName, "98thPercentile"));
assertEquals(histogram.getStatistics().getQuantile(0.99), mBeanServer.getAttribute(objectName, "99thPercentile"));
assertEquals(histogram.getStatistics().getQuantile(0.999), mBeanServer.getAttribute(objectName, "999thPercentile"));
} finally {
if (registry != null) {
registry.shutdown();
}
}
}
/**
* Tests that meters are properly reported via the JMXReporter.
*/
@Test
public void testMeterReporting() throws Exception {
MetricRegistry registry = null;
String meterName = "meter";
try {
Configuration config = new Configuration();
config.setString(MetricOptions.REPORTERS_LIST, "jmx_test");
config.setString(ConfigConstants.METRICS_REPORTER_PREFIX + "jmx_test." + ConfigConstants.METRICS_REPORTER_CLASS_SUFFIX, JMXReporter.class.getName());
registry = new MetricRegistry(MetricRegistryConfiguration.fromConfiguration(config));
TaskManagerMetricGroup metricGroup = new TaskManagerMetricGroup(registry, "localhost", "tmId");
TestMeter meter = new TestMeter();
metricGroup.meter(meterName, meter);
MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
ObjectName objectName = new ObjectName(JMX_DOMAIN_PREFIX + "taskmanager." + meterName, JMXReporter.generateJmxTable(metricGroup.getAllVariables()));
MBeanInfo info = mBeanServer.getMBeanInfo(objectName);
MBeanAttributeInfo[] attributeInfos = info.getAttributes();
assertEquals(2, attributeInfos.length);
assertEquals(meter.getRate(), mBeanServer.getAttribute(objectName, "Rate"));
assertEquals(meter.getCount(), mBeanServer.getAttribute(objectName, "Count"));
} finally {
if (registry != null) {
registry.shutdown();
}
}
}
}