diff options
author | Ola Aunronning <olaa@yahooinc.com> | 2023-08-28 15:29:20 +0200 |
---|---|---|
committer | Ola Aunronning <olaa@yahooinc.com> | 2023-08-28 15:29:20 +0200 |
commit | 16c3f00730d73504a855879693094160e9ef820c (patch) | |
tree | 249c9b0331bb33b9d20e6d68f68eac83958599a0 | |
parent | de1692aad9de5d06a64c5231be8be01a0357f854 (diff) |
Support fetching metrics in JSONL format
5 files changed, 49 insertions, 7 deletions
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/yamas/YamasHandler.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/yamas/YamasHandler.java index ef23a5ad070..fc5c790db54 100644 --- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/yamas/YamasHandler.java +++ b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/yamas/YamasHandler.java @@ -59,18 +59,18 @@ public class YamasHandler extends HttpHandlerBase { @Override public Optional<HttpResponse> doHandle(URI requestUri, Path apiPath, String consumer) { if (apiPath.matches(V1_PATH)) return Optional.of(resourceListResponse(requestUri, List.of(VALUES_PATH, CONSUMERS_PATH))); - if (apiPath.matches(VALUES_PATH)) return Optional.of(valuesResponse(consumer)); + if (apiPath.matches(VALUES_PATH)) return Optional.of(valuesResponse(consumer, requestUri.getQuery())); if (apiPath.matches(CONSUMERS_PATH)) return Optional.of(consumersResponse()); return Optional.empty(); } - private HttpResponse valuesResponse(String consumer) { + private HttpResponse valuesResponse(String consumer, String query) { try { List<MetricsPacket> metrics = new ArrayList<>(consumer == null ? valuesFetcher.fetchAllMetrics() : valuesFetcher.fetch(consumer)); if (consumer == null || "Vespa".equalsIgnoreCase(consumer)) { metrics.addAll(nodeMetricGatherer.gatherMetrics()); // TODO: Currently only add these metrics in this handler. Eventually should be included in all handlers } - return new YamasResponse(OK, metrics); + return new YamasResponse(OK, metrics, useJsonl(query)); } catch (JsonRenderingException e) { return new ErrorResponse(INTERNAL_SERVER_ERROR, e.getMessage()); } @@ -93,4 +93,8 @@ public class YamasHandler extends HttpHandlerBase { }; } + private boolean useJsonl(String query) { + return query != null && query.contains("jsonl=true"); + } + }
\ No newline at end of file diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/yamas/YamasResponse.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/yamas/YamasResponse.java index 6c94de49140..e838987133f 100644 --- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/yamas/YamasResponse.java +++ b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/yamas/YamasResponse.java @@ -16,10 +16,12 @@ import java.util.List; public class YamasResponse extends HttpResponse { private final List<MetricsPacket> metrics; + private boolean useJsonl; - public YamasResponse(int code, List<MetricsPacket> metrics) { + public YamasResponse(int code, List<MetricsPacket> metrics, boolean useJsonl) { super(code); this.metrics = metrics; + this.useJsonl = useJsonl; } @Override @@ -29,7 +31,10 @@ public class YamasResponse extends HttpResponse { @Override public void render(OutputStream outputStream) throws IOException { - YamasJsonUtil.toJson(metrics, outputStream, false); + if (useJsonl) + YamasJsonUtil.toJsonl(metrics, outputStream, false); + else + YamasJsonUtil.toJson(metrics, outputStream, false); } } diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/json/YamasJsonUtil.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/json/YamasJsonUtil.java index 5086846293b..39589e144e6 100644 --- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/json/YamasJsonUtil.java +++ b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/json/YamasJsonUtil.java @@ -8,6 +8,7 @@ import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.StreamWriteFeature; +import com.fasterxml.jackson.core.util.MinimalPrettyPrinter; import com.fasterxml.jackson.databind.ObjectMapper; import java.io.ByteArrayOutputStream; @@ -113,6 +114,15 @@ public class YamasJsonUtil { generator.close(); } + public static void toJsonl(List<MetricsPacket> metrics, OutputStream outputStream, boolean addStatus) throws IOException { + JsonGenerator generator = factory.createGenerator(outputStream) + .setPrettyPrinter(new MinimalPrettyPrinter("\n")); + for (MetricsPacket metricsPacket : metrics) { + toJson(metricsPacket, generator, addStatus); + } + generator.close(); + } + private static void toJson(MetricsPacket metric, JsonGenerator generator, boolean addStatus) throws IOException { generator.writeStartObject(); if (addStatus) { diff --git a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/http/yamas/YamasHandlerTest.java b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/http/yamas/YamasHandlerTest.java index 346fc6a462b..1d198a40ba8 100644 --- a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/http/yamas/YamasHandlerTest.java +++ b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/http/yamas/YamasHandlerTest.java @@ -54,4 +54,11 @@ public class YamasHandlerTest extends HttpHandlerTestBase { assertFalse(valuesResponse.contains("status_msg")); } + @Test + public void allows_fetching_jsonl_metrics() { + assertTrue(valuesResponse.startsWith("{\"metrics\":[{\"timestamp\":")); + valuesResponse = testDriver.sendRequest(VALUES_URI + "?jsonl=true").readAll(); + assertTrue(valuesResponse.startsWith("{\"timestamp\":")); + } + } diff --git a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/metric/model/json/YamasJsonModelTest.java b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/metric/model/json/YamasJsonModelTest.java index 3e85166430d..559f6e8457a 100644 --- a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/metric/model/json/YamasJsonModelTest.java +++ b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/metric/model/json/YamasJsonModelTest.java @@ -35,7 +35,7 @@ public class YamasJsonModelTest { YamasJsonModel jsonModel = getYamasJsonModel("yamas-array.json"); try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { - YamasResponse response = new YamasResponse(200, List.of(YamasJsonUtil.toMetricsPacketBuilder(jsonModel).build())); + YamasResponse response = new YamasResponse(200, List.of(YamasJsonUtil.toMetricsPacketBuilder(jsonModel).build()), false); response.render(outputStream); assertEquals(EXPECTED_JSON, outputStream.toString()); } @@ -52,7 +52,7 @@ public class YamasJsonModelTest { // Serialize and verify try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { - YamasResponse response = new YamasResponse(200, List.of(YamasJsonUtil.toMetricsPacketBuilder(jsonModel).build())); + YamasResponse response = new YamasResponse(200, List.of(YamasJsonUtil.toMetricsPacketBuilder(jsonModel).build()), false); response.render(outputStream); assertEquals(EXPECTED_JSON, outputStream.toString()); } @@ -85,6 +85,22 @@ public class YamasJsonModelTest { assertNull(jsonModel.routing); } + @Test + public void creates_correct_jsonl() throws IOException { + YamasJsonModel jsonModel = getYamasJsonModel("yamas-array.json"); + MetricsPacket packet = YamasJsonUtil.toMetricsPacketBuilder(jsonModel).build(); + // Add packet twice to verify object delimiter + List<MetricsPacket> metricPackets = List.of(packet, packet); + try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { + YamasResponse response = new YamasResponse(200, metricPackets, true); + response.render(outputStream); + assertEquals(""" + {"timestamp":1400047900,"application":"vespa.searchnode","metrics":{"cpu":55.5555555555555,"memory_virt":22222222222,"memory_rss":5555555555},"dimensions":{"applicationName":"app","tenantName":"tenant","metrictype":"system","instance":"searchnode","applicationInstance":"default","clustername":"cluster"},"routing":{"yamas":{"namespaces":["Vespa"]}}} + {"timestamp":1400047900,"application":"vespa.searchnode","metrics":{"cpu":55.5555555555555,"memory_virt":22222222222,"memory_rss":5555555555},"dimensions":{"applicationName":"app","tenantName":"tenant","metrictype":"system","instance":"searchnode","applicationInstance":"default","clustername":"cluster"},"routing":{"yamas":{"namespaces":["Vespa"]}}}""", + outputStream.toString()); + } + } + private YamasJsonModel getYamasJsonModel(String testFile) throws IOException { String filename = getClass().getClassLoader().getResource(testFile).getFile(); BufferedReader reader = Files.newBufferedReader(Paths.get(filename)); |