diff options
Diffstat (limited to 'container-core/src')
6 files changed, 464 insertions, 102 deletions
diff --git a/container-core/src/main/java/com/yahoo/container/jdisc/state/MetricsPacketsHandler.java b/container-core/src/main/java/com/yahoo/container/jdisc/state/MetricsPacketsHandler.java new file mode 100644 index 00000000000..50c64341581 --- /dev/null +++ b/container-core/src/main/java/com/yahoo/container/jdisc/state/MetricsPacketsHandler.java @@ -0,0 +1,188 @@ +// 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.google.inject.Inject; +import com.yahoo.collections.Tuple2; +import com.yahoo.component.provider.ComponentRegistry; +import com.yahoo.container.jdisc.state.StateHandler.JSONObjectWithLegibleException; +import com.yahoo.jdisc.Request; +import com.yahoo.jdisc.Response; +import com.yahoo.jdisc.Timer; +import com.yahoo.jdisc.handler.AbstractRequestHandler; +import com.yahoo.jdisc.handler.ContentChannel; +import com.yahoo.jdisc.handler.ResponseDispatch; +import com.yahoo.jdisc.handler.ResponseHandler; +import com.yahoo.jdisc.http.HttpHeaders; +import com.yahoo.metrics.MetricsPresentationConfig; +import org.json.JSONException; +import org.json.JSONObject; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import static com.yahoo.container.jdisc.state.StateHandler.getSnapshotPreprocessor; + +/** + * This handler outputs metrics in a json-like format. Each individual metric is a json object (packet), + * but there is no outer array or object that wraps the metrics packets. This handler is not set up by + * default, but can be added to the applications's services configuration. + * + * Based on {@link StateHandler}. + * + * @author gjoranv + */ +public class MetricsPacketsHandler extends AbstractRequestHandler { + static final String APPLICATION_KEY = "application"; + static final String TIMESTAMP_KEY = "timestamp"; + static final String STATUS_CODE_KEY = "status_code"; + static final String STATUS_MSG_KEY = "status_msg"; + static final String METRICS_KEY = "metrics"; + static final String DIMENSIONS_KEY = "dimensions"; + + + private final StateMonitor monitor; + private final Timer timer; + private final SnapshotProvider snapshotPreprocessor; + private final String applicationName; + + @Inject + public MetricsPacketsHandler(StateMonitor monitor, + Timer timer, + ComponentRegistry<SnapshotProvider> preprocessors, + MetricsPresentationConfig presentation, + MetricsPacketsHandlerConfig config) { + this.monitor = monitor; + this.timer = timer; + snapshotPreprocessor = getSnapshotPreprocessor(preprocessors, presentation); + applicationName = config.application(); + } + + + @Override + public ContentChannel handleRequest(Request request, ResponseHandler handler) { + new ResponseDispatch() { + @Override + protected Response newResponse() { + Response response = new Response(Response.Status.OK); + response.headers().add(HttpHeaders.Names.CONTENT_TYPE, "application/json"); + return response; + } + + @Override + protected Iterable<ByteBuffer> responseContent() { + return Collections.singleton(ByteBuffer.wrap(buildMetricOutput())); + } + }.dispatch(handler); + + return null; + } + + private byte[] buildMetricOutput() { + try { + String output = getStatusPacket() + getAllMetricsPackets(); + return output.getBytes(StandardCharsets.UTF_8); + } catch (JSONException e) { + throw new RuntimeException("Bad JSON construction.", e); + } + } + + /** + * Exactly one status packet is added to the response. + */ + private String getStatusPacket() throws JSONException { + JSONObject packet = new JSONObjectWithLegibleException(); + packet.put(APPLICATION_KEY, applicationName); + + StateMonitor.Status status = monitor.status(); + packet.put(STATUS_CODE_KEY, status.ordinal()); + packet.put(STATUS_MSG_KEY, status.name()); + return jsonToString(packet); + } + + private String jsonToString(JSONObject jsonObject) throws JSONException { + return jsonObject.toString(4); + } + + private String getAllMetricsPackets() throws JSONException { + StringBuilder ret = new StringBuilder(); + List<JSONObject> metricsPackets = getPacketsForSnapshot(getSnapshot(), applicationName, timer.currentTimeMillis()); + for (JSONObject packet : metricsPackets) { + ret.append("\n\n"); // For legibility and parsing in unit tests + ret.append(jsonToString(packet)); + } + return ret.toString(); + } + + private MetricSnapshot getSnapshot() { + if (snapshotPreprocessor == null) { + return monitor.snapshot(); + } else { + return snapshotPreprocessor.latestSnapshot(); + } + } + + private List<JSONObject> getPacketsForSnapshot(MetricSnapshot metricSnapshot, String application, long timestamp) throws JSONException { + if (metricSnapshot == null) return Collections.emptyList(); + + List<JSONObject> packets = new ArrayList<>(); + + for (Map.Entry<MetricDimensions, MetricSet> snapshotEntry : metricSnapshot) { + MetricDimensions metricDimensions = snapshotEntry.getKey(); + MetricSet metricSet = snapshotEntry.getValue(); + + JSONObjectWithLegibleException packet = new JSONObjectWithLegibleException(); + addMetaData(timestamp, application, packet); + addDimensions(metricDimensions, packet); + addMetrics(metricSet, packet); + packets.add(packet); + } + return packets; + } + + private void addMetaData(long timestamp, String application, JSONObjectWithLegibleException packet) { + packet.put(APPLICATION_KEY, application); + packet.put(TIMESTAMP_KEY, timestamp); + + } + + private void addDimensions(MetricDimensions metricDimensions, JSONObjectWithLegibleException packet) throws JSONException { + Iterator<Map.Entry<String, String>> dimensionsIterator = metricDimensions.iterator(); + if (dimensionsIterator.hasNext()) { + JSONObject jsonDim = new JSONObjectWithLegibleException(); + packet.put(DIMENSIONS_KEY, jsonDim); + for (Map.Entry<String, String> dimensionEntry : metricDimensions) { + jsonDim.put(dimensionEntry.getKey(), dimensionEntry.getValue()); + } + } + } + + private void addMetrics(MetricSet metricSet, JSONObjectWithLegibleException packet) throws JSONException { + JSONObjectWithLegibleException metrics = new JSONObjectWithLegibleException(); + packet.put(METRICS_KEY, metrics); + for (Map.Entry<String, MetricValue> metric : metricSet) { + String name = metric.getKey(); + MetricValue value = metric.getValue(); + if (value instanceof CountMetric) { + metrics.put(name + ".count", ((CountMetric) value).getCount()); + } else if (value instanceof GaugeMetric) { + GaugeMetric gauge = (GaugeMetric) value; + metrics.put(name + ".average", gauge.getAverage()) + .put(name + ".last", gauge.getLast()) + .put(name + ".max", gauge.getMax()); + if (gauge.getPercentiles().isPresent()) { + for (Tuple2<String, Double> prefixAndValue : gauge.getPercentiles().get()) { + metrics.put(name + "." + prefixAndValue.first + "percentile", prefixAndValue.second.doubleValue()); + } + } + } else { + throw new UnsupportedOperationException("Unknown metric class: " + value.getClass().getName()); + } + } + } + +} diff --git a/container-core/src/main/java/com/yahoo/container/jdisc/state/StateHandler.java b/container-core/src/main/java/com/yahoo/container/jdisc/state/StateHandler.java index 23c247fa438..da88a338049 100644 --- a/container-core/src/main/java/com/yahoo/container/jdisc/state/StateHandler.java +++ b/container-core/src/main/java/com/yahoo/container/jdisc/state/StateHandler.java @@ -54,11 +54,15 @@ public class StateHandler extends AbstractRequestHandler { this.monitor = monitor; this.timer = timer; this.config = buildConfigOutput(config); + snapshotPreprocessor = getSnapshotPreprocessor(preprocessors, presentation); + } + + static SnapshotProvider getSnapshotPreprocessor(ComponentRegistry<SnapshotProvider> preprocessors, MetricsPresentationConfig presentation) { List<SnapshotProvider> allPreprocessors = preprocessors.allComponents(); if (presentation.slidingwindow() && allPreprocessors.size() > 0) { - snapshotPreprocessor = allPreprocessors.get(0); + return allPreprocessors.get(0); } else { - snapshotPreprocessor = null; + return null; } } @@ -298,7 +302,7 @@ public class StateHandler extends AbstractRequestHandler { } /** Produces a flat list of metric entries from a snapshot (which organizes metrics by dimensions) */ - private static List<Tuple> flattenAllMetrics(MetricSnapshot snapshot) { + static List<Tuple> flattenAllMetrics(MetricSnapshot snapshot) { List<Tuple> metrics = new ArrayList<>(); for (Map.Entry<MetricDimensions, MetricSet> snapshotEntry : snapshot) { for (Map.Entry<String, MetricValue> metricSetEntry : snapshotEntry.getValue()) { @@ -308,7 +312,7 @@ public class StateHandler extends AbstractRequestHandler { return metrics; } - private static class Tuple { + static class Tuple { final MetricDimensions dim; final String key; diff --git a/container-core/src/main/resources/configdefinitions/metrics-packets-handler.def b/container-core/src/main/resources/configdefinitions/metrics-packets-handler.def new file mode 100644 index 00000000000..c295954fc5d --- /dev/null +++ b/container-core/src/main/resources/configdefinitions/metrics-packets-handler.def @@ -0,0 +1,6 @@ +# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +namespace=container.jdisc.state + +# The name of the application that is reporting metrics. +application string 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()); + } + +} |