/*
* 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++;
}
}
}