/* * 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.runtime.metrics; import akka.actor.ActorNotFound; import akka.actor.ActorRef; import akka.actor.ActorSystem; import org.apache.flink.configuration.ConfigConstants; import org.apache.flink.configuration.Configuration; import org.apache.flink.configuration.MetricOptions; import org.apache.flink.metrics.Counter; import org.apache.flink.metrics.Metric; import org.apache.flink.metrics.MetricConfig; import org.apache.flink.metrics.MetricGroup; import org.apache.flink.metrics.reporter.MetricReporter; import org.apache.flink.metrics.reporter.Scheduled; import org.apache.flink.runtime.akka.AkkaUtils; import org.apache.flink.runtime.metrics.groups.TaskManagerMetricGroup; import org.apache.flink.runtime.metrics.scope.ScopeFormats; import org.apache.flink.runtime.metrics.util.TestReporter; import org.apache.flink.util.TestLogger; import org.junit.Assert; import org.junit.Test; import scala.concurrent.Await; import scala.concurrent.duration.FiniteDuration; import java.util.List; import java.util.concurrent.TimeUnit; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; public class MetricRegistryTest extends TestLogger { private static final char GLOBAL_DEFAULT_DELIMITER = '.'; @Test public void testIsShutdown() { MetricRegistry metricRegistry = new MetricRegistry(MetricRegistryConfiguration.defaultMetricRegistryConfiguration()); Assert.assertFalse(metricRegistry.isShutdown()); metricRegistry.shutdown(); Assert.assertTrue(metricRegistry.isShutdown()); } /** * Verifies that the reporter class argument is correctly used to instantiate and open the reporter. */ @Test public void testReporterInstantiation() { Configuration config = new Configuration(); config.setString(MetricOptions.REPORTERS_LIST, "test"); config.setString(ConfigConstants.METRICS_REPORTER_PREFIX + "test." + ConfigConstants.METRICS_REPORTER_CLASS_SUFFIX, TestReporter1.class.getName()); MetricRegistry metricRegistry = new MetricRegistry(MetricRegistryConfiguration.fromConfiguration(config)); assertTrue(metricRegistry.getReporters().size() == 1); Assert.assertTrue(TestReporter1.wasOpened); metricRegistry.shutdown(); } protected static class TestReporter1 extends TestReporter { public static boolean wasOpened = false; @Override public void open(MetricConfig config) { wasOpened = true; } } /** * Verifies that multiple reporters are instantiated correctly. */ @Test public void testMultipleReporterInstantiation() { Configuration config = new Configuration(); config.setString(MetricOptions.REPORTERS_LIST, "test1, test2,test3"); config.setString(ConfigConstants.METRICS_REPORTER_PREFIX + "test1." + ConfigConstants.METRICS_REPORTER_CLASS_SUFFIX, TestReporter11.class.getName()); config.setString(ConfigConstants.METRICS_REPORTER_PREFIX + "test2." + ConfigConstants.METRICS_REPORTER_CLASS_SUFFIX, TestReporter12.class.getName()); config.setString(ConfigConstants.METRICS_REPORTER_PREFIX + "test3." + ConfigConstants.METRICS_REPORTER_CLASS_SUFFIX, TestReporter13.class.getName()); MetricRegistry metricRegistry = new MetricRegistry(MetricRegistryConfiguration.fromConfiguration(config)); assertTrue(metricRegistry.getReporters().size() == 3); Assert.assertTrue(TestReporter11.wasOpened); Assert.assertTrue(TestReporter12.wasOpened); Assert.assertTrue(TestReporter13.wasOpened); metricRegistry.shutdown(); } protected static class TestReporter11 extends TestReporter { public static boolean wasOpened = false; @Override public void open(MetricConfig config) { wasOpened = true; } } protected static class TestReporter12 extends TestReporter { public static boolean wasOpened = false; @Override public void open(MetricConfig config) { wasOpened = true; } } protected static class TestReporter13 extends TestReporter { public static boolean wasOpened = false; @Override public void open(MetricConfig config) { wasOpened = true; } } /** * Verifies that configured arguments are properly forwarded to the reporter. */ @Test public void testReporterArgumentForwarding() { Configuration config = new Configuration(); config.setString(MetricOptions.REPORTERS_LIST, "test"); config.setString(ConfigConstants.METRICS_REPORTER_PREFIX + "test." + ConfigConstants.METRICS_REPORTER_CLASS_SUFFIX, TestReporter2.class.getName()); config.setString(ConfigConstants.METRICS_REPORTER_PREFIX + "test.arg1", "hello"); config.setString(ConfigConstants.METRICS_REPORTER_PREFIX + "test.arg2", "world"); new MetricRegistry(MetricRegistryConfiguration.fromConfiguration(config)).shutdown(); } protected static class TestReporter2 extends TestReporter { @Override public void open(MetricConfig config) { Assert.assertEquals("hello", config.getString("arg1", null)); Assert.assertEquals("world", config.getString("arg2", null)); } } /** * Verifies that reporters implementing the Scheduled interface are regularly called to report the metrics. * * @throws InterruptedException */ @Test public void testReporterScheduling() throws InterruptedException { Configuration config = new Configuration(); config.setString(MetricOptions.REPORTERS_LIST, "test"); config.setString(ConfigConstants.METRICS_REPORTER_PREFIX + "test." + ConfigConstants.METRICS_REPORTER_CLASS_SUFFIX, TestReporter3.class.getName()); config.setString(ConfigConstants.METRICS_REPORTER_PREFIX + "test.arg1", "hello"); config.setString(ConfigConstants.METRICS_REPORTER_PREFIX + "test." + ConfigConstants.METRICS_REPORTER_INTERVAL_SUFFIX, "50 MILLISECONDS"); MetricRegistry registry = new MetricRegistry(MetricRegistryConfiguration.fromConfiguration(config)); long start = System.currentTimeMillis(); for (int x = 0; x < 10; x++) { Thread.sleep(100); int reportCount = TestReporter3.reportCount; long curT = System.currentTimeMillis(); /** * Within a given time-frame T only T/500 reports may be triggered due to the interval between reports. * This value however does not not take the first triggered report into account (=> +1). * Furthermore we have to account for the mis-alignment between reports being triggered and our time * measurement (=> +1); for T=200 a total of 4-6 reports may have been * triggered depending on whether the end of the interval for the first reports ends before * or after T=50. */ long maxAllowedReports = (curT - start) / 50 + 2; Assert.assertTrue("Too many report were triggered.", maxAllowedReports >= reportCount); } Assert.assertTrue("No report was triggered.", TestReporter3.reportCount > 0); registry.shutdown(); } protected static class TestReporter3 extends TestReporter implements Scheduled { public static int reportCount = 0; @Override public void report() { reportCount++; } } /** * Verifies that reporters are notified of added/removed metrics. */ @Test public void testReporterNotifications() { Configuration config = new Configuration(); config.setString(MetricOptions.REPORTERS_LIST, "test1,test2"); config.setString(ConfigConstants.METRICS_REPORTER_PREFIX + "test1." + ConfigConstants.METRICS_REPORTER_CLASS_SUFFIX, TestReporter6.class.getName()); config.setString(ConfigConstants.METRICS_REPORTER_PREFIX + "test2." + ConfigConstants.METRICS_REPORTER_CLASS_SUFFIX, TestReporter7.class.getName()); MetricRegistry registry = new MetricRegistry(MetricRegistryConfiguration.fromConfiguration(config)); TaskManagerMetricGroup root = new TaskManagerMetricGroup(registry, "host", "id"); root.counter("rootCounter"); root.close(); assertTrue(TestReporter6.addCalled); assertTrue(TestReporter6.removeCalled); assertTrue(TestReporter7.addCalled); assertTrue(TestReporter7.removeCalled); registry.shutdown(); } protected static class TestReporter6 extends TestReporter { public static boolean addCalled = false; public static boolean removeCalled = false; @Override public void notifyOfAddedMetric(Metric metric, String metricName, MetricGroup group) { addCalled = true; assertTrue(metric instanceof Counter); assertEquals("rootCounter", metricName); } @Override public void notifyOfRemovedMetric(Metric metric, String metricName, MetricGroup group) { removeCalled = true; Assert.assertTrue(metric instanceof Counter); Assert.assertEquals("rootCounter", metricName); } } protected static class TestReporter7 extends TestReporter { public static boolean addCalled = false; public static boolean removeCalled = false; @Override public void notifyOfAddedMetric(Metric metric, String metricName, MetricGroup group) { addCalled = true; assertTrue(metric instanceof Counter); assertEquals("rootCounter", metricName); } @Override public void notifyOfRemovedMetric(Metric metric, String metricName, MetricGroup group) { removeCalled = true; Assert.assertTrue(metric instanceof Counter); Assert.assertEquals("rootCounter", metricName); } } /** * Verifies that the scope configuration is properly extracted. */ @Test public void testScopeConfig() { Configuration config = new Configuration(); config.setString(MetricOptions.SCOPE_NAMING_TM, "A"); config.setString(MetricOptions.SCOPE_NAMING_TM_JOB, "B"); config.setString(MetricOptions.SCOPE_NAMING_TASK, "C"); config.setString(MetricOptions.SCOPE_NAMING_OPERATOR, "D"); ScopeFormats scopeConfig = MetricRegistryConfiguration.createScopeConfig(config); assertEquals("A", scopeConfig.getTaskManagerFormat().format()); assertEquals("B", scopeConfig.getTaskManagerJobFormat().format()); assertEquals("C", scopeConfig.getTaskFormat().format()); assertEquals("D", scopeConfig.getOperatorFormat().format()); } @Test public void testConfigurableDelimiter() { Configuration config = new Configuration(); config.setString(MetricOptions.SCOPE_DELIMITER, "_"); config.setString(MetricOptions.SCOPE_NAMING_TM, "A.B.C.D.E"); MetricRegistry registry = new MetricRegistry(MetricRegistryConfiguration.fromConfiguration(config)); TaskManagerMetricGroup tmGroup = new TaskManagerMetricGroup(registry, "host", "id"); assertEquals("A_B_C_D_E_name", tmGroup.getMetricIdentifier("name")); registry.shutdown(); } @Test public void testConfigurableDelimiterForReporters() { Configuration config = new Configuration(); config.setString(MetricOptions.REPORTERS_LIST, "test1,test2,test3"); config.setString(ConfigConstants.METRICS_REPORTER_PREFIX + "test1." + ConfigConstants.METRICS_REPORTER_SCOPE_DELIMITER, "_"); config.setString(ConfigConstants.METRICS_REPORTER_PREFIX + "test1." + ConfigConstants.METRICS_REPORTER_CLASS_SUFFIX, TestReporter.class.getName()); config.setString(ConfigConstants.METRICS_REPORTER_PREFIX + "test2." + ConfigConstants.METRICS_REPORTER_SCOPE_DELIMITER, "-"); config.setString(ConfigConstants.METRICS_REPORTER_PREFIX + "test2." + ConfigConstants.METRICS_REPORTER_CLASS_SUFFIX, TestReporter.class.getName()); config.setString(ConfigConstants.METRICS_REPORTER_PREFIX + "test3." + ConfigConstants.METRICS_REPORTER_SCOPE_DELIMITER, "AA"); config.setString(ConfigConstants.METRICS_REPORTER_PREFIX + "test3." + ConfigConstants.METRICS_REPORTER_CLASS_SUFFIX, TestReporter.class.getName()); MetricRegistry registry = new MetricRegistry(MetricRegistryConfiguration.fromConfiguration(config)); assertEquals(GLOBAL_DEFAULT_DELIMITER, registry.getDelimiter()); assertEquals('_', registry.getDelimiter(0)); assertEquals('-', registry.getDelimiter(1)); assertEquals(GLOBAL_DEFAULT_DELIMITER, registry.getDelimiter(2)); assertEquals(GLOBAL_DEFAULT_DELIMITER, registry.getDelimiter(3)); assertEquals(GLOBAL_DEFAULT_DELIMITER, registry.getDelimiter(-1)); registry.shutdown(); } @Test public void testConfigurableDelimiterForReportersInGroup() { Configuration config = new Configuration(); config.setString(MetricOptions.REPORTERS_LIST, "test1,test2,test3,test4"); config.setString(ConfigConstants.METRICS_REPORTER_PREFIX + "test1." + ConfigConstants.METRICS_REPORTER_SCOPE_DELIMITER, "_"); config.setString(ConfigConstants.METRICS_REPORTER_PREFIX + "test1." + ConfigConstants.METRICS_REPORTER_CLASS_SUFFIX, TestReporter8.class.getName()); config.setString(ConfigConstants.METRICS_REPORTER_PREFIX + "test2." + ConfigConstants.METRICS_REPORTER_SCOPE_DELIMITER, "-"); config.setString(ConfigConstants.METRICS_REPORTER_PREFIX + "test2." + ConfigConstants.METRICS_REPORTER_CLASS_SUFFIX, TestReporter8.class.getName()); config.setString(ConfigConstants.METRICS_REPORTER_PREFIX + "test3." + ConfigConstants.METRICS_REPORTER_SCOPE_DELIMITER, "AA"); config.setString(ConfigConstants.METRICS_REPORTER_PREFIX + "test3." + ConfigConstants.METRICS_REPORTER_CLASS_SUFFIX, TestReporter8.class.getName()); config.setString(ConfigConstants.METRICS_REPORTER_PREFIX + "test4." + ConfigConstants.METRICS_REPORTER_CLASS_SUFFIX, TestReporter8.class.getName()); config.setString(MetricOptions.SCOPE_NAMING_TM, "A.B"); MetricRegistry registry = new MetricRegistry(MetricRegistryConfiguration.fromConfiguration(config)); List<MetricReporter> reporters = registry.getReporters(); ((TestReporter8)reporters.get(0)).expectedDelimiter = '_'; //test1 reporter ((TestReporter8)reporters.get(1)).expectedDelimiter = '-'; //test2 reporter ((TestReporter8)reporters.get(2)).expectedDelimiter = GLOBAL_DEFAULT_DELIMITER; //test3 reporter, because 'AA' - not correct delimiter ((TestReporter8)reporters.get(3)).expectedDelimiter = GLOBAL_DEFAULT_DELIMITER; //for test4 reporter use global delimiter TaskManagerMetricGroup group = new TaskManagerMetricGroup(registry, "host", "id"); group.counter("C"); group.close(); registry.shutdown(); assertEquals(4, TestReporter8.numCorrectDelimitersForRegister); assertEquals(4, TestReporter8.numCorrectDelimitersForUnregister); } /** * Tests that the query actor will be stopped when the MetricRegistry is shut down. */ @Test public void testQueryActorShutdown() throws Exception { final FiniteDuration timeout = new FiniteDuration(10L, TimeUnit.SECONDS); MetricRegistry registry = new MetricRegistry(MetricRegistryConfiguration.defaultMetricRegistryConfiguration()); final ActorSystem actorSystem = AkkaUtils.createDefaultActorSystem(); registry.startQueryService(actorSystem, null); ActorRef queryServiceActor = registry.getQueryService(); registry.shutdown(); try { Await.result(actorSystem.actorSelection(queryServiceActor.path()).resolveOne(timeout), timeout); fail("The query actor should be terminated resulting in a ActorNotFound exception."); } catch (ActorNotFound e) { // we expect the query actor to be shut down } } public static class TestReporter8 extends TestReporter { char expectedDelimiter; public static int numCorrectDelimitersForRegister = 0; public static int numCorrectDelimitersForUnregister = 0; @Override public void notifyOfAddedMetric(Metric metric, String metricName, MetricGroup group) { String expectedMetric = "A" + expectedDelimiter + "B" + expectedDelimiter + "C"; assertEquals(expectedMetric, group.getMetricIdentifier(metricName, this)); assertEquals(expectedMetric, group.getMetricIdentifier(metricName)); numCorrectDelimitersForRegister++; } @Override public void notifyOfRemovedMetric(Metric metric, String metricName, MetricGroup group) { String expectedMetric = "A" + expectedDelimiter + "B" + expectedDelimiter + "C"; assertEquals(expectedMetric, group.getMetricIdentifier(metricName, this)); assertEquals(expectedMetric, group.getMetricIdentifier(metricName)); numCorrectDelimitersForUnregister++; } } }