aboutsummaryrefslogtreecommitdiffstats
path: root/metrics-proxy/src
diff options
context:
space:
mode:
authorHenning Baldersheim <balder@yahoo-inc.com>2021-03-09 08:12:21 +0100
committerHenning Baldersheim <balder@yahoo-inc.com>2021-03-09 08:12:21 +0100
commit97d8918b2805ec3c54be7aff8c888c2a9d5ca9ad (patch)
treee6222a2a0aa844eb4fb76162d9d6cbd3fad831f5 /metrics-proxy/src
parentb04ec57706b4d30711f76ebb8cb7fc8c8469b0a9 (diff)
Move the parser out to prepare for simpler testing.
Diffstat (limited to 'metrics-proxy/src')
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/MetricsParser.java154
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/RemoteMetricsFetcher.java144
2 files changed, 155 insertions, 143 deletions
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/MetricsParser.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/MetricsParser.java
new file mode 100644
index 00000000000..f9445e5b26a
--- /dev/null
+++ b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/MetricsParser.java
@@ -0,0 +1,154 @@
+// Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.metricsproxy.service;
+
+import ai.vespa.metricsproxy.metric.Metric;
+import ai.vespa.metricsproxy.metric.Metrics;
+import ai.vespa.metricsproxy.metric.model.DimensionId;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import static ai.vespa.metricsproxy.metric.model.DimensionId.toDimensionId;
+
+/**
+ * Fetch metrics for a given vespa service
+ *
+ * @author Jo Kristian Bergum
+ */
+public class MetricsParser {
+
+ private static final ObjectMapper jsonMapper = new ObjectMapper();
+
+ static Metrics parse(String data) throws IOException {
+ JsonParser parser = jsonMapper.createParser(data);
+
+ if (parser.nextToken() != JsonToken.START_OBJECT) {
+ throw new IOException("Expected start of object, got " + parser.currentToken());
+ }
+
+ Metrics metrics = new Metrics();
+ for (parser.nextToken(); parser.getCurrentToken() != JsonToken.END_OBJECT; parser.nextToken()) {
+ String fieldName = parser.getCurrentName();
+ JsonToken token = parser.nextToken();
+ if (fieldName.equals("metrics")) {
+ metrics = parseMetrics(parser);
+ } else {
+ if (token == JsonToken.START_OBJECT || token == JsonToken.START_ARRAY) {
+ parser.skipChildren();
+ }
+ }
+ }
+ return metrics;
+ }
+
+ static private Metrics parseSnapshot(JsonParser parser) throws IOException {
+ if (parser.getCurrentToken() != JsonToken.START_OBJECT) {
+ throw new IOException("Expected start of 'snapshot' object, got " + parser.currentToken());
+ }
+ Metrics metrics = new Metrics();
+ for (parser.nextToken(); parser.getCurrentToken() != JsonToken.END_OBJECT; parser.nextToken()) {
+ String fieldName = parser.getCurrentName();
+ JsonToken token = parser.nextToken();
+ if (fieldName.equals("to")) {
+ long timestamp = parser.getLongValue();
+ long now = System.currentTimeMillis() / 1000;
+ timestamp = Metric.adjustTime(timestamp, now);
+ metrics = new Metrics(timestamp);
+ } else {
+ if (token == JsonToken.START_OBJECT || token == JsonToken.START_ARRAY) {
+ parser.skipChildren();
+ }
+ }
+ }
+ return metrics;
+ }
+
+ static private void parseValues(JsonParser parser, Metrics metrics) throws IOException {
+ if (parser.getCurrentToken() != JsonToken.START_ARRAY) {
+ throw new IOException("Expected start of 'metrics:values' array, got " + parser.currentToken());
+ }
+
+ Map<String, Map<DimensionId, String>> uniqueDimensions = new HashMap<>();
+ while (parser.nextToken() == JsonToken.START_OBJECT) {
+ // read everything from this START_OBJECT to the matching END_OBJECT
+ // and return it as a tree model ObjectNode
+ JsonNode value = jsonMapper.readTree(parser);
+ handleValue(value, metrics.getTimeStamp(), metrics, uniqueDimensions);
+
+ // do whatever you need to do with this object
+ }
+ }
+
+ static private Metrics parseMetrics(JsonParser parser) throws IOException {
+ if (parser.getCurrentToken() != JsonToken.START_OBJECT) {
+ throw new IOException("Expected start of 'metrics' object, got " + parser.currentToken());
+ }
+ Metrics metrics = new Metrics();
+ for (parser.nextToken(); parser.getCurrentToken() != JsonToken.END_OBJECT; parser.nextToken()) {
+ String fieldName = parser.getCurrentName();
+ JsonToken token = parser.nextToken();
+ if (fieldName.equals("snapshot")) {
+ metrics = parseSnapshot(parser);
+ } else if (fieldName.equals("values")) {
+ parseValues(parser, metrics);
+ } else {
+ if (token == JsonToken.START_OBJECT || token == JsonToken.START_ARRAY) {
+ parser.skipChildren();
+ }
+ }
+ }
+ return metrics;
+ }
+
+ static private void handleValue(JsonNode metric, long timestamp, Metrics metrics, Map<String, Map<DimensionId, String>> uniqueDimensions) {
+ String name = metric.get("name").textValue();
+ String description = "";
+
+ if (metric.has("description")) {
+ description = metric.get("description").textValue();
+ }
+
+ Map<DimensionId, String> dim = Collections.emptyMap();
+ if (metric.has("dimensions")) {
+ JsonNode dimensions = metric.get("dimensions");
+ StringBuilder sb = new StringBuilder();
+ for (Iterator<?> it = dimensions.fieldNames(); it.hasNext(); ) {
+ String k = (String) it.next();
+ String v = dimensions.get(k).asText();
+ sb.append(toDimensionId(k)).append(v);
+ }
+ if ( ! uniqueDimensions.containsKey(sb.toString())) {
+ dim = new HashMap<>();
+ for (Iterator<?> it = dimensions.fieldNames(); it.hasNext(); ) {
+ String k = (String) it.next();
+ String v = dimensions.get(k).textValue();
+ dim.put(toDimensionId(k), v);
+ }
+ uniqueDimensions.put(sb.toString(), Collections.unmodifiableMap(dim));
+ }
+ dim = uniqueDimensions.get(sb.toString());
+ }
+
+ JsonNode aggregates = metric.get("values");
+ for (Iterator<?> it = aggregates.fieldNames(); it.hasNext(); ) {
+ String aggregator = (String) it.next();
+ JsonNode aggregatorValue = aggregates.get(aggregator);
+ if (aggregatorValue == null) {
+ throw new IllegalArgumentException("Value for aggregator '" + aggregator + "' is missing");
+ }
+ Number value = aggregatorValue.numberValue();
+ if (value == null) {
+ throw new IllegalArgumentException("Value for aggregator '" + aggregator + "' is not a number");
+ }
+ StringBuilder metricName = (new StringBuilder()).append(name).append(".").append(aggregator);
+ metrics.add(new Metric(metricName.toString(), value, timestamp, dim, description));
+ }
+ }
+}
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/RemoteMetricsFetcher.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/RemoteMetricsFetcher.java
index 54976f70e55..314d556b9b4 100644
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/RemoteMetricsFetcher.java
+++ b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/RemoteMetricsFetcher.java
@@ -1,21 +1,9 @@
// Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package ai.vespa.metricsproxy.service;
-import ai.vespa.metricsproxy.metric.Metric;
import ai.vespa.metricsproxy.metric.Metrics;
-import ai.vespa.metricsproxy.metric.model.DimensionId;
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.core.JsonToken;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-
-import static ai.vespa.metricsproxy.metric.model.DimensionId.toDimensionId;
/**
* Fetch metrics for a given vespa service
@@ -24,8 +12,6 @@ import static ai.vespa.metricsproxy.metric.model.DimensionId.toDimensionId;
*/
public class RemoteMetricsFetcher extends HttpMetricFetcher {
- private static final ObjectMapper jsonMapper = new ObjectMapper();
-
final static String METRICS_PATH = STATE_PATH + "metrics";
RemoteMetricsFetcher(VespaService service, int port) {
@@ -46,142 +32,14 @@ public class RemoteMetricsFetcher extends HttpMetricFetcher {
return createMetrics(data, fetchCount);
}
- /**
- * Connect to remote service over http and fetch metrics
- */
Metrics createMetrics(String data, int fetchCount) {
Metrics remoteMetrics = new Metrics();
try {
- remoteMetrics = parse(data);
+ remoteMetrics = MetricsParser.parse(data);
} catch (Exception e) {
handleException(e, data, fetchCount);
}
return remoteMetrics;
}
-
- private Metrics parseSnapshot(JsonParser parser) throws IOException {
- if (parser.getCurrentToken() != JsonToken.START_OBJECT) {
- throw new IOException("Expected start of 'snapshot' object, got " + parser.currentToken());
- }
- Metrics metrics = new Metrics();
- for (parser.nextToken(); parser.getCurrentToken() != JsonToken.END_OBJECT; parser.nextToken()) {
- String fieldName = parser.getCurrentName();
- JsonToken token = parser.nextToken();
- if (fieldName.equals("to")) {
- long timestamp = parser.getLongValue();
- long now = System.currentTimeMillis() / 1000;
- timestamp = Metric.adjustTime(timestamp, now);
- metrics = new Metrics(timestamp);
- } else {
- if (token == JsonToken.START_OBJECT || token == JsonToken.START_ARRAY) {
- parser.skipChildren();
- }
- }
- }
- return metrics;
- }
-
- private void parseValues(JsonParser parser, Metrics metrics) throws IOException {
- if (parser.getCurrentToken() != JsonToken.START_ARRAY) {
- throw new IOException("Expected start of 'metrics:values' array, got " + parser.currentToken());
- }
-
- Map<String, Map<DimensionId, String>> uniqueDimensions = new HashMap<>();
- while (parser.nextToken() == JsonToken.START_OBJECT) {
- // read everything from this START_OBJECT to the matching END_OBJECT
- // and return it as a tree model ObjectNode
- JsonNode value = jsonMapper.readTree(parser);
- handleValue(value, metrics.getTimeStamp(), metrics, uniqueDimensions);
-
- // do whatever you need to do with this object
- }
- }
-
- private Metrics parseMetrics(JsonParser parser) throws IOException {
- if (parser.getCurrentToken() != JsonToken.START_OBJECT) {
- throw new IOException("Expected start of 'metrics' object, got " + parser.currentToken());
- }
- Metrics metrics = new Metrics();
- for (parser.nextToken(); parser.getCurrentToken() != JsonToken.END_OBJECT; parser.nextToken()) {
- String fieldName = parser.getCurrentName();
- JsonToken token = parser.nextToken();
- if (fieldName.equals("snapshot")) {
- metrics = parseSnapshot(parser);
- } else if (fieldName.equals("values")) {
- parseValues(parser, metrics);
- } else {
- if (token == JsonToken.START_OBJECT || token == JsonToken.START_ARRAY) {
- parser.skipChildren();
- }
- }
- }
- return metrics;
- }
- private Metrics parse(String data) throws IOException {
- JsonParser parser = jsonMapper.createParser(data);
-
- if (parser.nextToken() != JsonToken.START_OBJECT) {
- throw new IOException("Expected start of object, got " + parser.currentToken());
- }
-
- Metrics metrics = new Metrics();
- for (parser.nextToken(); parser.getCurrentToken() != JsonToken.END_OBJECT; parser.nextToken()) {
- String fieldName = parser.getCurrentName();
- JsonToken token = parser.nextToken();
- if (fieldName.equals("metrics")) {
- metrics = parseMetrics(parser);
- } else {
- if (token == JsonToken.START_OBJECT || token == JsonToken.START_ARRAY) {
- parser.skipChildren();
- }
- }
- }
- return metrics;
- }
-
- private void handleValue(JsonNode metric, long timestamp, Metrics metrics, Map<String, Map<DimensionId, String>> uniqueDimensions) {
- String name = metric.get("name").textValue();
- String description = "";
-
- if (metric.has("description")) {
- description = metric.get("description").textValue();
- }
-
- Map<DimensionId, String> dim = Collections.emptyMap();
- if (metric.has("dimensions")) {
- JsonNode dimensions = metric.get("dimensions");
- StringBuilder sb = new StringBuilder();
- for (Iterator<?> it = dimensions.fieldNames(); it.hasNext(); ) {
- String k = (String) it.next();
- String v = dimensions.get(k).asText();
- sb.append(toDimensionId(k)).append(v);
- }
- if ( ! uniqueDimensions.containsKey(sb.toString())) {
- dim = new HashMap<>();
- for (Iterator<?> it = dimensions.fieldNames(); it.hasNext(); ) {
- String k = (String) it.next();
- String v = dimensions.get(k).textValue();
- dim.put(toDimensionId(k), v);
- }
- uniqueDimensions.put(sb.toString(), Collections.unmodifiableMap(dim));
- }
- dim = uniqueDimensions.get(sb.toString());
- }
-
- JsonNode aggregates = metric.get("values");
- for (Iterator<?> it = aggregates.fieldNames(); it.hasNext(); ) {
- String aggregator = (String) it.next();
- JsonNode aggregatorValue = aggregates.get(aggregator);
- if (aggregatorValue == null) {
- throw new IllegalArgumentException("Value for aggregator '" + aggregator + "' is missing");
- }
- Number value = aggregatorValue.numberValue();
- if (value == null) {
- throw new IllegalArgumentException("Value for aggregator '" + aggregator + "' is not a number");
- }
- StringBuilder metricName = (new StringBuilder()).append(name).append(".").append(aggregator);
- metrics.add(new Metric(metricName.toString(), value, timestamp, dim, description));
- }
- }
}