summaryrefslogtreecommitdiffstats
path: root/metrics-proxy
diff options
context:
space:
mode:
authorgjoranv <gv@verizonmedia.com>2019-12-19 15:44:55 +0100
committergjoranv <gv@verizonmedia.com>2019-12-20 02:30:17 +0100
commitcc973932ac847f4ad66cb68a149e0683af428548 (patch)
treebe2f962ca29ed47049aa5d17f7f71d70d2e1b629 /metrics-proxy
parent60f91ea8c4778e3bd08963dec3d500d8482ef0d4 (diff)
Add JSON model for full application output
- Multiple nodes yielding one GenericJsonModel each.
Diffstat (limited to 'metrics-proxy')
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/json/GenericApplicationModel.java36
-rw-r--r--metrics-proxy/src/test/java/ai/vespa/metricsproxy/metric/model/json/GenericApplicationModelTest.java122
-rw-r--r--metrics-proxy/src/test/resources/generic-application.json90
3 files changed, 248 insertions, 0 deletions
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/json/GenericApplicationModel.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/json/GenericApplicationModel.java
new file mode 100644
index 00000000000..b3ebd469b13
--- /dev/null
+++ b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/json/GenericApplicationModel.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+ */
+
+package ai.vespa.metricsproxy.metric.model.json;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import java.io.IOException;
+import java.util.List;
+
+import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_ABSENT;
+
+/**
+ * @author gjoranv
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+@JsonInclude(NON_ABSENT)
+public class GenericApplicationModel {
+
+ @JsonProperty("nodes")
+ public List<GenericJsonModel> nodes;
+
+ public String serialize() {
+ ObjectMapper mapper = JacksonUtil.createObjectMapper();
+ try {
+ return mapper.writeValueAsString(this);
+ } catch (IOException e) {
+ throw new JsonRenderingException("Could not render application nodes. Check the log for details.", e);
+ }
+ }
+
+}
diff --git a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/metric/model/json/GenericApplicationModelTest.java b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/metric/model/json/GenericApplicationModelTest.java
new file mode 100644
index 00000000000..eac63998402
--- /dev/null
+++ b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/metric/model/json/GenericApplicationModelTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+ */
+
+package ai.vespa.metricsproxy.metric.model.json;
+
+import ai.vespa.metricsproxy.http.application.Node;
+import ai.vespa.metricsproxy.metric.model.MetricsPacket;
+import ai.vespa.metricsproxy.metric.model.ServiceId;
+import ai.vespa.metricsproxy.metric.model.StatusCode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+import static ai.vespa.metricsproxy.TestUtil.getFileContents;
+import static ai.vespa.metricsproxy.metric.ExternalMetrics.VESPA_NODE_SERVICE_ID;
+import static ai.vespa.metricsproxy.metric.model.DimensionId.toDimensionId;
+import static ai.vespa.metricsproxy.metric.model.MetricId.toMetricId;
+import static ai.vespa.metricsproxy.metric.model.ServiceId.toServiceId;
+import static ai.vespa.metricsproxy.metric.model.json.JacksonUtil.createObjectMapper;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+/**
+ * @author gjoranv
+ */
+public class GenericApplicationModelTest {
+ private static final String TEST_FILE = "generic-application.json";
+
+ @Test
+ public void deserialize_serialize_roundtrip() throws IOException {
+ GenericApplicationModel model = genericJsonModelFromTestFile();
+
+ // Do some sanity checking
+ assertEquals(2, model.nodes.size());
+ GenericJsonModel node0Model = model.nodes.get(0);
+ assertEquals("node0", node0Model.node.name);
+ assertEquals(1, node0Model.services.size());
+ GenericService service = node0Model.services.get(0);
+ assertEquals(1, service.metrics.size());
+ assertEquals(4, service.metrics.get(0).values.get("queries.count"), 0.001d);
+
+ GenericJsonModel node1Model = model.nodes.get(1);
+ GenericNode node1 = node1Model.node;
+ assertEquals("node1", node1.name);
+ assertEquals(32.444, node1.metrics.get(0).values.get("cpu.util"), 0.001d);
+
+ assertThatSerializedModelEqualsTestFile(model);
+ }
+
+ @Test
+ public void metrics_packets_can_be_converted_to_generic_json_model() throws Exception {
+ var nodePacket = new MetricsPacket.Builder(VESPA_NODE_SERVICE_ID)
+ .timestamp(123456L)
+ .putMetric(toMetricId("node-metric"), 1.234)
+ .putDimension(toDimensionId("node-dim"), "node-dim-value")
+ .build();
+
+ var servicePacket = new MetricsPacket.Builder(toServiceId("my-service"))
+ .timestamp(123456L)
+ .statusCode(0)
+ .putMetric(toMetricId("service-metric"), 1234)
+ .putDimension(toDimensionId("service-dim"), "service-dim-value")
+ .build();
+
+
+ var metricsByNode = Map.of(toNode("node0"), List.of(nodePacket, servicePacket));
+
+ GenericApplicationModel model = GenericJsonUtil.toGenericApplicationModel(metricsByNode);
+
+ GenericJsonModel nodeModel = model.nodes.get(0);
+ assertNotNull(nodeModel.node);
+ assertEquals("node0", nodeModel.node.name);
+ assertEquals(1, nodeModel.node.metrics.size());
+ GenericMetrics nodeMetrics = nodeModel.node.metrics.get(0);
+ assertEquals(1.234, nodeMetrics.values.get("node-metric"), 0.001d);
+ assertEquals("node-dim-value", nodeMetrics.dimensions.get("node-dim"));
+
+ assertEquals(1, nodeModel.services.size());
+ GenericService service = nodeModel.services.get(0);
+ assertEquals(StatusCode.UP.status, service.status.code);
+ assertEquals("", service.status.description);
+
+ assertEquals(1, service.metrics.size());
+ GenericMetrics serviceMetrics = service.metrics.get(0);
+ assertEquals(1234L, serviceMetrics.values.get("service-metric").longValue());
+ assertEquals("service-dim-value", serviceMetrics.dimensions.get("service-dim"));
+
+ // Visual inspection
+ System.out.println(createObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(nodeModel));
+ }
+
+ private MetricsPacket createMetricsPacket(String service, Number metricsValue, boolean isNode) {
+ return new MetricsPacket.Builder(isNode ? VESPA_NODE_SERVICE_ID : toServiceId(service))
+ .timestamp(1234L)
+ .statusCode(0)
+ .putMetric(toMetricId(service + "-metric"), metricsValue)
+ .putDimension(toDimensionId(service + "-dim"), isNode ? "node-dim-value" : "service-dim-value")
+ .build();
+
+ }
+
+ private static void assertThatSerializedModelEqualsTestFile(GenericApplicationModel model) {
+ String serialized = model.serialize();
+ String trimmed = serialized.trim().replaceAll("\\s+", "");
+
+ String expected = getFileContents(TEST_FILE).trim().replaceAll("\\s+", "");
+ assertEquals(expected, trimmed);
+ }
+
+ private static GenericApplicationModel genericJsonModelFromTestFile() throws IOException {
+ ObjectMapper mapper = createObjectMapper();
+ return mapper.readValue(getFileContents(TEST_FILE), GenericApplicationModel.class);
+ }
+
+ private static Node toNode(String name) {
+ return new Node(name, "host", 0, "path");
+ }
+}
diff --git a/metrics-proxy/src/test/resources/generic-application.json b/metrics-proxy/src/test/resources/generic-application.json
new file mode 100644
index 00000000000..231f86a9b3b
--- /dev/null
+++ b/metrics-proxy/src/test/resources/generic-application.json
@@ -0,0 +1,90 @@
+{
+ "nodes": [
+ {
+ "node": {
+ "name": "node0",
+ "timestamp": 1234,
+ "metrics": [
+ {
+ "values": {
+ "cpu.util": 16.222
+ },
+ "dimensions": {
+ "state": "active"
+ }
+ }
+ ]
+ },
+ "services": [
+ {
+ "name": "searchnode",
+ "timestamp": 1234,
+ "status": {
+ "code": "up"
+ },
+ "metrics": [
+ {
+ "values": {
+ "queries.count": 4
+ },
+ "dimensions": {
+ "documentType": "music"
+ }
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "node": {
+ "name": "node1",
+ "timestamp": 1234,
+ "metrics": [
+ {
+ "values": {
+ "cpu.util": 32.444
+ },
+ "dimensions": {
+ "state": "active"
+ }
+ }
+ ]
+ },
+ "services": [
+ {
+ "name": "searchnode",
+ "timestamp": 1234,
+ "status": {
+ "code": "up"
+ },
+ "metrics": [
+ {
+ "values": {
+ "queries.count": 8
+ },
+ "dimensions": {
+ "documentType": "music"
+ }
+ }
+ ]
+ },
+ {
+ "name": "slobrok",
+ "timestamp": 1234,
+ "status": {
+ "code": "unknown",
+ "description": "Unable to fetch metrics from service 'slobrok'"
+ },
+ "metrics": [
+ {
+ "values": {},
+ "dimensions": {
+ "instance": "slobrok0"
+ }
+ }
+ ]
+ }
+ ]
+ }
+ ]
+} \ No newline at end of file