diff options
author | gjoranv <gv@oath.com> | 2018-10-24 11:06:39 +0200 |
---|---|---|
committer | gjoranv <gv@oath.com> | 2018-10-31 12:40:12 +0100 |
commit | a7cf143f91aa3d07efc79b683d9c996fb406129a (patch) | |
tree | 7ec655cf23036764e19302e0223f5f435909e00f /container-core/src/test/java/com/yahoo/container/jdisc/state | |
parent | af146b406da7911a0e035ea3bf184680b31bac9b (diff) |
Add new handler for obtaining metrics snapshot in "packets" format
Diffstat (limited to 'container-core/src/test/java/com/yahoo/container/jdisc/state')
3 files changed, 262 insertions, 98 deletions
diff --git a/container-core/src/test/java/com/yahoo/container/jdisc/state/MetricsPacketsHandlerTest.java b/container-core/src/test/java/com/yahoo/container/jdisc/state/MetricsPacketsHandlerTest.java new file mode 100644 index 00000000000..895cb885828 --- /dev/null +++ b/container-core/src/test/java/com/yahoo/container/jdisc/state/MetricsPacketsHandlerTest.java @@ -0,0 +1,145 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.container.jdisc.state; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.yahoo.jdisc.Metric; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static com.yahoo.container.jdisc.state.MetricsPacketsHandler.APPLICATION_KEY; +import static com.yahoo.container.jdisc.state.MetricsPacketsHandler.DIMENSIONS_KEY; +import static com.yahoo.container.jdisc.state.MetricsPacketsHandler.METRICS_KEY; +import static com.yahoo.container.jdisc.state.MetricsPacketsHandler.STATUS_CODE_KEY; +import static com.yahoo.container.jdisc.state.MetricsPacketsHandler.STATUS_MSG_KEY; +import static com.yahoo.container.jdisc.state.MetricsPacketsHandler.TIMESTAMP_KEY; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * @author gjoranv + */ +public class MetricsPacketsHandlerTest extends StateHandlerTestBase { + + @Test + public void only_status_packet_is_returned_prior_to_first_snapshot() throws Exception { + metric.add("not_included", 1, null); + String response = requestAsString("http://localhost/metrics-packets"); + + List<JsonNode> packets = toJsonPackets(response); + assertEquals(1, packets.size()); + + JsonNode statusPacket = packets.get(0); + assertEquals(statusPacket.toString(), APPLICATION_NAME, statusPacket.get(APPLICATION_KEY).asText()); + assertEquals(statusPacket.toString(), 0, statusPacket.get(STATUS_CODE_KEY).asInt()); + assertEquals(statusPacket.toString(), "up", statusPacket.get(STATUS_MSG_KEY).asText()); + } + + @Test + public void metrics_are_included_after_snapshot() throws Exception { + metric.add("counter", 1, null); + incrementCurrentTimeAndAssertSnapshot(SNAPSHOT_INTERVAL); + String response = requestAsString("http://localhost/metrics-packets"); + + List<JsonNode> packets = toJsonPackets(response); + assertEquals(2, packets.size()); + + JsonNode counterPacket = packets.get(1); + assertCountMetric(counterPacket, "counter.count", 1); + } + + @Test + public void metadata_is_included_in_each_metrics_packet() throws Exception { + metric.add("counter", 1, null); + incrementCurrentTimeAndAssertSnapshot(SNAPSHOT_INTERVAL); + String response = requestAsString("http://localhost/metrics-packets"); + + List<JsonNode> packets = toJsonPackets(response); + JsonNode counterPacket = packets.get(1); + + assertTrue(counterPacket.has(TIMESTAMP_KEY)); + assertTrue(counterPacket.has(APPLICATION_KEY)); + assertEquals(APPLICATION_NAME, counterPacket.get(APPLICATION_KEY).asText()); + } + + @Test + public void expected_aggregators_are_output_for_gauge_metrics() throws Exception{ + Metric.Context context = metric.createContext(Collections.singletonMap("dim1", "value1")); + metric.set("gauge", 0.2, null); + incrementCurrentTimeAndAssertSnapshot(SNAPSHOT_INTERVAL); + String response = requestAsString("http://localhost/metrics-packets"); + + List<JsonNode> packets = toJsonPackets(response); + JsonNode gaugeMetric = packets.get(1).get(METRICS_KEY); + assertEquals(0.2, gaugeMetric.get("gauge.last").asDouble(), 0.1); + assertEquals(0.2, gaugeMetric.get("gauge.average").asDouble(), 0.1); + assertEquals(0.2, gaugeMetric.get("gauge.max").asDouble(), 0.1); + } + + @Test + public void dimensions_from_context_are_included() throws Exception { + Metric.Context context = metric.createContext(Collections.singletonMap("dim1", "value1")); + metric.add("counter", 1, context); + incrementCurrentTimeAndAssertSnapshot(SNAPSHOT_INTERVAL); + String response = requestAsString("http://localhost/metrics-packets"); + + List<JsonNode> packets = toJsonPackets(response); + JsonNode counterPacket = packets.get(1); + + assertTrue(counterPacket.has(DIMENSIONS_KEY)); + JsonNode dimensions = counterPacket.get(DIMENSIONS_KEY); + assertEquals("value1", dimensions.get("dim1").asText()); + } + + @Test + public void metrics_with_identical_dimensions_are_contained_in_the_same_packet() throws Exception { + Metric.Context context = metric.createContext(Collections.singletonMap("dim1", "value1")); + metric.add("counter1", 1, context); + metric.add("counter2", 2, context); + incrementCurrentTimeAndAssertSnapshot(SNAPSHOT_INTERVAL); + String response = requestAsString("http://localhost/metrics-packets"); + + List<JsonNode> packets = toJsonPackets(response); + assertEquals(2, packets.size()); + JsonNode countersPacket = packets.get(1); + + assertEquals("value1", countersPacket.get(DIMENSIONS_KEY).get("dim1").asText()); + + assertCountMetric(countersPacket, "counter1.count", 1); + assertCountMetric(countersPacket, "counter2.count", 2); + } + + @Test + public void metrics_with_different_dimensions_get_separate_packets() throws Exception { + Metric.Context context1 = metric.createContext(Collections.singletonMap("dim1", "value1")); + Metric.Context context2 = metric.createContext(Collections.singletonMap("dim2", "value2")); + metric.add("counter1", 1, context1); + metric.add("counter2", 1, context2); + incrementCurrentTimeAndAssertSnapshot(SNAPSHOT_INTERVAL); + String response = requestAsString("http://localhost/metrics-packets"); + + List<JsonNode> packets = toJsonPackets(response); + assertEquals(3, packets.size()); + } + + private List<JsonNode> toJsonPackets(String response) throws Exception { + List<JsonNode> jsonPackets = new ArrayList<>(); + String[] packets = response.split("\\n\\n"); + ObjectMapper mapper = new ObjectMapper(); + for (String packet : packets) { + jsonPackets.add(mapper.readTree(mapper.getFactory().createParser(packet))); + } + return jsonPackets; + } + + private void assertCountMetric(JsonNode metricsPacket, String metricName, long expected) { + assertTrue(metricsPacket.has(METRICS_KEY)); + JsonNode counterMetrics = metricsPacket.get(METRICS_KEY); + assertTrue(counterMetrics.has(metricName)); + assertEquals(expected, counterMetrics.get(metricName).asLong()); + } + +} diff --git a/container-core/src/test/java/com/yahoo/container/jdisc/state/StateHandlerTest.java b/container-core/src/test/java/com/yahoo/container/jdisc/state/StateHandlerTest.java index 41b195baeb3..b3a19e77391 100644 --- a/container-core/src/test/java/com/yahoo/container/jdisc/state/StateHandlerTest.java +++ b/container-core/src/test/java/com/yahoo/container/jdisc/state/StateHandlerTest.java @@ -2,92 +2,24 @@ package com.yahoo.container.jdisc.state; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.inject.AbstractModule; import com.yahoo.component.Vtag; -import com.yahoo.container.core.ApplicationMetadataConfig; -import com.yahoo.container.jdisc.config.HealthMonitorConfig; import com.yahoo.jdisc.Metric; -import com.yahoo.jdisc.Response; -import com.yahoo.jdisc.Timer; -import com.yahoo.jdisc.application.ContainerBuilder; -import com.yahoo.jdisc.application.MetricConsumer; -import com.yahoo.jdisc.handler.BufferedContentChannel; -import com.yahoo.jdisc.handler.ContentChannel; -import com.yahoo.jdisc.handler.ResponseHandler; -import com.yahoo.jdisc.test.TestDriver; -import com.yahoo.metrics.MetricsPresentationConfig; import com.yahoo.vespa.defaults.Defaults; -import org.junit.After; -import org.junit.Before; import org.junit.Test; -import java.io.InputStreamReader; -import java.io.Reader; -import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.TreeMap; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; /** * @author Simon Thoresen Hult */ -public class StateHandlerTest { - - private final static long SNAPSHOT_INTERVAL = TimeUnit.SECONDS.toMillis(300); - private final static long META_GENERATION = 69; - private TestDriver driver; - private StateMonitor monitor; - private Metric metric; - private final AtomicLong currentTimeMillis = new AtomicLong(0); - - @Before - public void startTestDriver() { - Timer timer = this.currentTimeMillis::get; - this.driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(new AbstractModule() { - @Override - protected void configure() { - bind(Timer.class).toInstance(timer); - } - }); - ContainerBuilder builder = driver.newContainerBuilder(); - HealthMonitorConfig healthMonitorConfig = - new HealthMonitorConfig( - new HealthMonitorConfig.Builder() - .snapshot_interval(TimeUnit.MILLISECONDS.toSeconds(SNAPSHOT_INTERVAL))); - ThreadFactory threadFactory = ignored -> mock(Thread.class); - this.monitor = new StateMonitor(healthMonitorConfig, timer, threadFactory); - builder.guiceModules().install(new AbstractModule() { - - @Override - protected void configure() { - bind(StateMonitor.class).toInstance(monitor); - bind(MetricConsumer.class).toProvider(MetricConsumerProviders.wrap(monitor)); - bind(ApplicationMetadataConfig.class).toInstance(new ApplicationMetadataConfig( - new ApplicationMetadataConfig.Builder().generation(META_GENERATION))); - bind(MetricsPresentationConfig.class) - .toInstance(new MetricsPresentationConfig(new MetricsPresentationConfig.Builder())); - } - }); - builder.serverBindings().bind("http://*/*", builder.getInstance(StateHandler.class)); - driver.activateContainer(builder); - metric = builder.getInstance(Metric.class); - } - - @After - public void stopTestDriver() { - assertTrue(driver.close()); - } +public class StateHandlerTest extends StateHandlerTestBase { @Test public void testReportPriorToFirstSnapshot() throws Exception { @@ -392,37 +324,8 @@ public class StateHandlerTest { assertEquals(Vtag.currentVersion.toString(), version.asText()); } - private void incrementCurrentTimeAndAssertSnapshot(long val) { - currentTimeMillis.addAndGet(val); - assertTrue("Expected a new snapshot to be generated", monitor.checkTime()); - } - private void incrementCurrentTimeAndAssertNoSnapshot(long val) { currentTimeMillis.addAndGet(val); assertFalse("Expected no snapshot", monitor.checkTime());; } - - private String requestAsString(String requestUri) throws Exception { - final BufferedContentChannel content = new BufferedContentChannel(); - Response response = driver.dispatchRequest(requestUri, new ResponseHandler() { - - @Override - public ContentChannel handleResponse(Response response) { - return content; - } - }).get(60, TimeUnit.SECONDS); - assertNotNull(response); - assertEquals(Response.Status.OK, response.getStatus()); - StringBuilder str = new StringBuilder(); - Reader in = new InputStreamReader(content.toStream(), StandardCharsets.UTF_8); - for (int c; (c = in.read()) != -1; ) { - str.append((char)c); - } - return str.toString(); - } - - private JsonNode requestAsJson(String requestUri) throws Exception { - ObjectMapper mapper = new ObjectMapper(); - return mapper.readTree(mapper.getFactory().createParser(requestAsString(requestUri))); - } } diff --git a/container-core/src/test/java/com/yahoo/container/jdisc/state/StateHandlerTestBase.java b/container-core/src/test/java/com/yahoo/container/jdisc/state/StateHandlerTestBase.java new file mode 100644 index 00000000000..a806df1883f --- /dev/null +++ b/container-core/src/test/java/com/yahoo/container/jdisc/state/StateHandlerTestBase.java @@ -0,0 +1,116 @@ +package com.yahoo.container.jdisc.state; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.inject.AbstractModule; +import com.yahoo.container.core.ApplicationMetadataConfig; +import com.yahoo.container.jdisc.config.HealthMonitorConfig; +import com.yahoo.jdisc.Metric; +import com.yahoo.jdisc.Response; +import com.yahoo.jdisc.Timer; +import com.yahoo.jdisc.application.ContainerBuilder; +import com.yahoo.jdisc.application.MetricConsumer; +import com.yahoo.jdisc.handler.BufferedContentChannel; +import com.yahoo.jdisc.handler.ContentChannel; +import com.yahoo.jdisc.handler.ResponseHandler; +import com.yahoo.jdisc.test.TestDriver; +import com.yahoo.metrics.MetricsPresentationConfig; +import org.junit.After; +import org.junit.Before; + +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; + +/** + * @author Simon Thoresen Hult + * @author gjoranv + */ +public class StateHandlerTestBase { + final static long SNAPSHOT_INTERVAL = TimeUnit.SECONDS.toMillis(300); + final static long META_GENERATION = 69; + static final String APPLICATION_NAME = "state-handler-test-base"; + TestDriver driver; + StateMonitor monitor; + Metric metric; + final AtomicLong currentTimeMillis = new AtomicLong(0); + + @Before + public void startTestDriver() { + Timer timer = this.currentTimeMillis::get; + this.driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(new AbstractModule() { + @Override + protected void configure() { + bind(Timer.class).toInstance(timer); + } + }); + ContainerBuilder builder = driver.newContainerBuilder(); + HealthMonitorConfig healthMonitorConfig = + new HealthMonitorConfig( + new HealthMonitorConfig.Builder() + .snapshot_interval(TimeUnit.MILLISECONDS.toSeconds(SNAPSHOT_INTERVAL))); + ThreadFactory threadFactory = ignored -> mock(Thread.class); + this.monitor = new StateMonitor(healthMonitorConfig, timer, threadFactory); + builder.guiceModules().install(new AbstractModule() { + + @Override + protected void configure() { + bind(StateMonitor.class).toInstance(monitor); + bind(MetricConsumer.class).toProvider(MetricConsumerProviders.wrap(monitor)); + bind(ApplicationMetadataConfig.class).toInstance(new ApplicationMetadataConfig( + new ApplicationMetadataConfig.Builder().generation(META_GENERATION))); + bind(MetricsPresentationConfig.class) + .toInstance(new MetricsPresentationConfig(new MetricsPresentationConfig.Builder())); + bind(MetricsPacketsHandlerConfig.class).toInstance(new MetricsPacketsHandlerConfig( + new MetricsPacketsHandlerConfig.Builder().application(APPLICATION_NAME))); + } + }); + builder.serverBindings().bind("http://*/*", builder.getInstance(StateHandler.class)); + builder.serverBindings().bind("http://*/metrics-packets", builder.getInstance(MetricsPacketsHandler.class)); + driver.activateContainer(builder); + metric = builder.getInstance(Metric.class); + } + + @After + public void stopTestDriver() { + assertTrue(driver.close()); + } + + String requestAsString(String requestUri) throws Exception { + final BufferedContentChannel content = new BufferedContentChannel(); + Response response = driver.dispatchRequest(requestUri, new ResponseHandler() { + + @Override + public ContentChannel handleResponse(Response response) { + return content; + } + }).get(60, TimeUnit.SECONDS); + assertNotNull(response); + assertEquals(Response.Status.OK, response.getStatus()); + StringBuilder str = new StringBuilder(); + Reader in = new InputStreamReader(content.toStream(), StandardCharsets.UTF_8); + for (int c; (c = in.read()) != -1; ) { + str.append((char)c); + } + return str.toString(); + } + + JsonNode requestAsJson(String requestUri) throws Exception { + ObjectMapper mapper = new ObjectMapper(); + return mapper.readTree(mapper.getFactory().createParser(requestAsString(requestUri))); + } + + void incrementCurrentTimeAndAssertSnapshot(long val) { + currentTimeMillis.addAndGet(val); + assertTrue("Expected a new snapshot to be generated", monitor.checkTime()); + } + +} |