| /* |
| * 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.ignite.util; |
| |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.Map; |
| import java.util.UUID; |
| import org.apache.ignite.internal.IgniteEx; |
| import org.apache.ignite.internal.commandline.CommandList; |
| import org.apache.ignite.internal.commandline.metric.MetricCommandArg; |
| import org.apache.ignite.internal.processors.metric.MetricRegistry; |
| import org.apache.ignite.internal.processors.metric.impl.HistogramMetricImpl; |
| import org.apache.ignite.internal.util.typedef.internal.U; |
| import org.junit.Test; |
| |
| import static java.util.regex.Pattern.quote; |
| import static org.apache.ignite.internal.commandline.CommandHandler.EXIT_CODE_INVALID_ARGUMENTS; |
| import static org.apache.ignite.internal.commandline.CommandHandler.EXIT_CODE_OK; |
| import static org.apache.ignite.internal.commandline.CommandList.METRIC; |
| import static org.apache.ignite.internal.commandline.metric.MetricCommandArg.NODE_ID; |
| import static org.apache.ignite.internal.commandline.systemview.SystemViewCommand.COLUMN_SEPARATOR; |
| import static org.apache.ignite.internal.processors.metric.GridMetricManager.IGNITE_METRICS; |
| import static org.apache.ignite.internal.processors.metric.GridMetricManager.SYS_METRICS; |
| import static org.apache.ignite.internal.processors.metric.impl.MetricUtils.SEPARATOR; |
| import static org.apache.ignite.internal.processors.metric.impl.MetricUtils.metricName; |
| import static org.apache.ignite.testframework.GridTestUtils.assertContains; |
| |
| /** Tests output of {@link CommandList#METRIC} command. */ |
| public class MetricCommandTest extends GridCommandHandlerClusterByClassAbstractTest { |
| /** Command line argument for printing metric values. */ |
| private static final String CMD_METRIC = METRIC.text(); |
| |
| /** Test node with 0 index. */ |
| private IgniteEx ignite0; |
| |
| /** {@inheritDoc} */ |
| @Override protected void beforeTest() throws Exception { |
| super.beforeTest(); |
| |
| injectTestSystemOut(); |
| |
| autoConfirmation = false; |
| |
| ignite0 = ignite(0); |
| } |
| |
| /** Tests command error output in case of mandatory metric name is omitted. */ |
| @Test |
| public void testMetricNameMissedFailure() { |
| assertContains(log, executeCommand(EXIT_CODE_INVALID_ARGUMENTS, CMD_METRIC), |
| "The name of a metric(metric registry) is expected."); |
| } |
| |
| /** Tests command error output in case value of {@link MetricCommandArg#NODE_ID} argument is omitted. */ |
| @Test |
| public void testNodeIdMissedFailure() { |
| assertContains(log, executeCommand(EXIT_CODE_INVALID_ARGUMENTS, CMD_METRIC, SYS_METRICS, NODE_ID.argName()), |
| "ID of the node from which metric values should be obtained is expected."); |
| } |
| |
| /** Tests command error output in case value of {@link MetricCommandArg#NODE_ID} argument is invalid.*/ |
| @Test |
| public void testInvalidNodeIdFailure() { |
| assertContains(log, |
| executeCommand(EXIT_CODE_INVALID_ARGUMENTS, CMD_METRIC, SYS_METRICS, NODE_ID.argName(), "invalid_node_id"), |
| "Failed to parse " + NODE_ID.argName() + |
| " command argument. String representation of \"java.util.UUID\" is exepected." + |
| " For example: 123e4567-e89b-42d3-a456-556642440000" |
| ); |
| } |
| |
| /** Tests command error output in case multiple metric names are specified. */ |
| @Test |
| public void testMultipleMetricNamesFailure() { |
| assertContains(log, |
| executeCommand(EXIT_CODE_INVALID_ARGUMENTS, CMD_METRIC, IGNITE_METRICS, SYS_METRICS), |
| "Multiple metric(metric registry) names are not supported."); |
| } |
| |
| /** Tests command error output in case {@link MetricCommandArg#NODE_ID} argument value refers to nonexistent node. */ |
| @Test |
| public void testNonExistentNodeIdFailure() { |
| String incorrectNodeId = UUID.randomUUID().toString(); |
| |
| assertContains(log, |
| executeCommand(EXIT_CODE_INVALID_ARGUMENTS, CMD_METRIC, "--node-id", incorrectNodeId, IGNITE_METRICS), |
| "Failed to perform operation.\nNode with id=" + incorrectNodeId + " not found"); |
| } |
| |
| /** Tests command output in case nonexistent metric name is specified. */ |
| @Test |
| public void testNonExistentMetric() { |
| assertContains(log, executeCommand(EXIT_CODE_OK, CMD_METRIC, IGNITE_METRICS + SEPARATOR), |
| "No metric with specified name was found [name=" + IGNITE_METRICS + SEPARATOR + ']'); |
| |
| assertContains(log, executeCommand(EXIT_CODE_OK, CMD_METRIC, "nonexistent.metric"), |
| "No metric with specified name was found [name=nonexistent.metric]"); |
| } |
| |
| /** */ |
| @Test |
| public void testHistogramMetrics() { |
| String mregName = "histogram-registry"; |
| |
| MetricRegistry mreg = ignite0.context().metric().registry(mregName); |
| |
| long[] bounds = new long[] {50, 500}; |
| |
| HistogramMetricImpl histogram = mreg.histogram("histogram", bounds, null); |
| |
| histogram.value(10); |
| histogram.value(51); |
| histogram.value(60); |
| histogram.value(600); |
| histogram.value(600); |
| histogram.value(600); |
| |
| histogram = mreg.histogram("histogram_with_underscore", bounds, null); |
| |
| histogram.value(10); |
| histogram.value(51); |
| histogram.value(60); |
| histogram.value(600); |
| histogram.value(600); |
| histogram.value(600); |
| |
| assertEquals("1", metric(ignite0, metricName(mregName, "histogram_0_50"))); |
| assertEquals("2", metric(ignite0, metricName(mregName, "histogram_50_500"))); |
| assertEquals("3", metric(ignite0, metricName(mregName, "histogram_500_inf"))); |
| assertEquals("[1, 2, 3]", metric(ignite0, metricName(mregName, "histogram"))); |
| |
| assertEquals("1", metric(ignite0, metricName(mregName, "histogram_with_underscore_0_50"))); |
| assertEquals("2", metric(ignite0, metricName(mregName, "histogram_with_underscore_50_500"))); |
| assertEquals("3", metric(ignite0, metricName(mregName, "histogram_with_underscore_500_inf"))); |
| assertEquals("[1, 2, 3]", metric(ignite0, metricName(mregName, "histogram_with_underscore"))); |
| } |
| |
| /** */ |
| @Test |
| public void testNodeIdArgument() { |
| String mregName = "boolean-metric-registry"; |
| |
| MetricRegistry mreg = ignite0.context().metric().registry(mregName); |
| |
| mreg.booleanMetric("boolean-metric", ""); |
| |
| mreg = ignite(1).context().metric().registry(mregName); |
| |
| mreg.booleanMetric("boolean-metric", "").value(true); |
| |
| assertEquals("false", metric(ignite0, metricName(mregName, "boolean-metric"))); |
| assertEquals("true", metric(ignite(1), metricName(mregName, "boolean-metric"))); |
| } |
| |
| /** */ |
| @Test |
| public void testRegistryMetrics() { |
| String mregName = "test-metric-registry"; |
| |
| MetricRegistry mreg = ignite0.context().metric().registry(mregName); |
| |
| mreg.booleanMetric("boolean-metric", ""); |
| mreg.longMetric("long-metric", "").increment(); |
| mreg.intMetric("int-metric", "").increment(); |
| mreg.doubleMetric("double-metric", ""); |
| mreg.hitRateMetric("hitrate-metric", "", getTestTimeout(), 2); |
| mreg.histogram("histogram", new long[] {50, 100}, null).value(10); |
| mreg.hitRateMetric("hitrate-metric", "", getTestTimeout(), 2); |
| mreg.objectMetric("object-metric", Object.class, "").value(new Object() { |
| @Override public String toString() { |
| return "test-object"; |
| } |
| }); |
| |
| Map<String, String> metrics = metrics(ignite0, mregName); |
| |
| assertEquals("0.0", metrics.get(metricName(mregName, "double-metric"))); |
| assertEquals("false", metrics.get(metricName(mregName, "boolean-metric"))); |
| assertEquals("1", metrics.get(metricName(mregName, "long-metric"))); |
| assertEquals("1", metrics.get(metricName(mregName, "int-metric"))); |
| assertEquals("test-object", metrics.get(metricName(mregName, "object-metric"))); |
| assertEquals("[1, 0, 0]", metrics.get(metricName(mregName, "histogram"))); |
| assertEquals("0", metric(ignite0, metricName(mregName, "hitrate-metric"))); |
| } |
| |
| /** */ |
| @Test |
| public void testBooleanMetrics() { |
| String mregName = "boolean-metric-registry"; |
| |
| MetricRegistry mreg = ignite0.context().metric().registry(mregName); |
| |
| mreg.booleanMetric("boolean-metric", ""); |
| |
| assertEquals("false", metric(ignite0, metricName(mregName, "boolean-metric"))); |
| |
| mreg.register("boolean-gauge", () -> true, ""); |
| |
| assertEquals("true", metric(ignite0, metricName(mregName, "boolean-gauge"))); |
| } |
| |
| /** */ |
| @Test |
| public void testLongMetrics() { |
| String mregName = "long-metric-registry"; |
| |
| MetricRegistry mreg = ignite0.context().metric().registry(mregName); |
| |
| mreg.longMetric("long-metric", "").add(Long.MAX_VALUE); |
| |
| assertEquals(Long.toString(Long.MAX_VALUE), metric(ignite0, metricName(mregName, "long-metric"))); |
| |
| mreg.register("long-gauge", () -> 0L, ""); |
| |
| assertEquals("0", metric(ignite0, metricName(mregName, "long-gauge"))); |
| } |
| |
| /** */ |
| @Test |
| public void testIntegerMetrics() { |
| String mregName = "int-metric-registry"; |
| |
| MetricRegistry mreg = ignite0.context().metric().registry(mregName); |
| |
| mreg.intMetric("int-metric", "").add(Integer.MAX_VALUE); |
| |
| assertEquals(Integer.toString(Integer.MAX_VALUE), metric(ignite0, metricName(mregName, "int-metric"))); |
| |
| mreg.register("int-gauge", () -> 0, ""); |
| |
| assertEquals("0", metric(ignite0, metricName(mregName, "int-gauge"))); |
| } |
| |
| /** */ |
| @Test |
| public void testDoubleMetrics() { |
| String mregName = "int-double-registry"; |
| |
| MetricRegistry mreg = ignite0.context().metric().registry(mregName); |
| |
| mreg.doubleMetric("double-metric", "").add(111.222); |
| |
| assertEquals("111.222", metric(ignite0, metricName(mregName, "double-metric"))); |
| |
| mreg.register("double-gauge", () -> 0D, ""); |
| |
| assertEquals("0.0", metric(ignite0, metricName(mregName, "double-gauge"))); |
| } |
| |
| /** */ |
| @Test |
| public void testObjectMetrics() { |
| String mregName = "object-registry"; |
| |
| MetricRegistry mreg = ignite0.context().metric().registry(mregName); |
| |
| Object metricVal = new Object() { |
| @Override public String toString() { |
| return "test-object"; |
| } |
| }; |
| |
| mreg.objectMetric("object-metric", Object.class, "").value(metricVal); |
| |
| assertEquals("test-object", metric(ignite0, metricName(mregName, "object-metric"))); |
| |
| mreg.register("object-gauge", () -> metricVal, Object.class, ""); |
| |
| assertEquals("test-object", metric(ignite0, metricName(mregName, "object-gauge"))); |
| } |
| |
| /** */ |
| @Test |
| public void testHitrateMetrics() { |
| String mregName = "hitrate-registry"; |
| |
| MetricRegistry mreg = ignite0.context().metric().registry(mregName); |
| |
| mreg.hitRateMetric("hitrate-metric", "", getTestTimeout(), 2).add(Integer.MAX_VALUE); |
| |
| assertEquals(Integer.toString(Integer.MAX_VALUE), metric(ignite0, metricName(mregName, "hitrate-metric"))); |
| } |
| |
| /** |
| * Gets metric values via command-line utility. |
| * |
| * @param node Node to obtain metric values from. |
| * @param name Name of a particular metric or metric registry. |
| * @return String representation of metric values. |
| */ |
| private Map<String, String> metrics(IgniteEx node, String name) { |
| String nodeId = node.context().discovery().localNode().id().toString(); |
| |
| String out = executeCommand(EXIT_CODE_OK, CMD_METRIC, name, NODE_ID.argName(), nodeId); |
| |
| Map<String, String> res = parseMetricCommandOutput(out); |
| |
| assertEquals("value", res.remove("metric")); |
| |
| return res; |
| } |
| |
| /** |
| * Gets single metric value via command-line utility. |
| * |
| * @param node Node to obtain metric from. |
| * @param name Name of the metric. |
| * @return String representation of metric value. |
| */ |
| private String metric(IgniteEx node, String name) { |
| Map<String, String> metrics = metrics(node, name); |
| |
| assertEquals(1, metrics.size()); |
| |
| return metrics.get(name); |
| } |
| |
| /** |
| * Obtains metric values from command output. |
| * |
| * @param out Command output to parse. |
| * @return Metric values. |
| */ |
| private Map<String, String> parseMetricCommandOutput(String out) { |
| String outStart = "--------------------------------------------------------------------------------"; |
| |
| String outEnd = "Command [" + METRIC.toCommandName() + "] finished with code: " + EXIT_CODE_OK; |
| |
| String[] rows = out.substring( |
| out.indexOf(outStart) + outStart.length() + 1, |
| out.indexOf(outEnd) - 1 |
| ).split(U.nl()); |
| |
| Map<String, String> res = new HashMap<>(); |
| |
| for (String row : rows) { |
| Iterator<String> iter = Arrays.stream(row.split(quote(COLUMN_SEPARATOR))) |
| .map(String::trim) |
| .filter(str -> !str.isEmpty()) |
| .iterator(); |
| |
| res.put(iter.next(), iter.next()); |
| } |
| |
| return res; |
| } |
| |
| /** |
| * Executes command and checks its exit code. |
| * |
| * @param expExitCode Expected exit code. |
| * @param args Command lines arguments. |
| * @return Result of command execution. |
| */ |
| private String executeCommand(int expExitCode, String... args) { |
| int res = execute(args); |
| |
| assertEquals(expExitCode, res); |
| |
| return testOut.toString(); |
| } |
| } |