aboutsummaryrefslogtreecommitdiffstats
path: root/metrics-proxy/src/main/java/ai/vespa
diff options
context:
space:
mode:
Diffstat (limited to 'metrics-proxy/src/main/java/ai/vespa')
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/MetricsJsonResponse.java31
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/PrometheusResponse.java35
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/application/ApplicationMetricsHandler.java11
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/metrics/MetricsV1Handler.java5
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/metrics/MetricsV2Handler.java5
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/prometheus/PrometheusHandler.java5
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/json/GenericApplicationModel.java11
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/json/GenericJsonModel.java11
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/prometheus/PrometheusModel.java12
-rw-r--r--metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/MetricsParser.java33
10 files changed, 126 insertions, 33 deletions
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/MetricsJsonResponse.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/MetricsJsonResponse.java
new file mode 100644
index 00000000000..b927db790b2
--- /dev/null
+++ b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/MetricsJsonResponse.java
@@ -0,0 +1,31 @@
+package ai.vespa.metricsproxy.http;
+
+import com.yahoo.container.jdisc.HttpResponse;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.function.Consumer;
+
+/**
+ * @author jonmv
+ */
+public class MetricsJsonResponse extends HttpResponse {
+
+ private final Consumer<OutputStream> modelWriter;
+
+ public MetricsJsonResponse(int status, Consumer<OutputStream> modelWriter) {
+ super(status);
+ this.modelWriter = modelWriter;
+ }
+
+ @Override
+ public void render(OutputStream outputStream) throws IOException {
+ modelWriter.accept(outputStream);
+ }
+
+ @Override
+ public long maxPendingBytes() {
+ return 1 << 20;
+ }
+
+}
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/PrometheusResponse.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/PrometheusResponse.java
new file mode 100644
index 00000000000..e0c74671c9c
--- /dev/null
+++ b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/PrometheusResponse.java
@@ -0,0 +1,35 @@
+package ai.vespa.metricsproxy.http;
+
+import ai.vespa.metricsproxy.metric.model.prometheus.PrometheusModel;
+import com.yahoo.container.jdisc.HttpResponse;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+
+/**
+ * @author jonmv
+ */
+public class PrometheusResponse extends HttpResponse {
+
+ private final PrometheusModel model;
+
+ public PrometheusResponse(int status, PrometheusModel model) {
+ super(status);
+ this.model = model;
+ }
+
+ @Override
+ public void render(OutputStream outputStream) throws IOException {
+ Writer writer = new OutputStreamWriter(outputStream);
+ model.serialize(writer);
+ writer.flush();
+ }
+
+ @Override
+ public long maxPendingBytes() {
+ return 1 << 20;
+ }
+
+}
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/application/ApplicationMetricsHandler.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/application/ApplicationMetricsHandler.java
index 58b51020bb9..ace0d0abc65 100644
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/application/ApplicationMetricsHandler.java
+++ b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/application/ApplicationMetricsHandler.java
@@ -3,7 +3,8 @@
package ai.vespa.metricsproxy.http.application;
import ai.vespa.metricsproxy.core.MetricsConsumers;
-import ai.vespa.metricsproxy.http.TextResponse;
+import ai.vespa.metricsproxy.http.MetricsJsonResponse;
+import ai.vespa.metricsproxy.http.PrometheusResponse;
import ai.vespa.metricsproxy.metric.model.ConsumerId;
import ai.vespa.metricsproxy.metric.model.DimensionId;
import ai.vespa.metricsproxy.metric.model.MetricsPacket;
@@ -62,12 +63,12 @@ public class ApplicationMetricsHandler extends HttpHandlerBase {
return Optional.empty();
}
- private JsonResponse applicationMetricsResponse(String requestedConsumer) {
+ private HttpResponse applicationMetricsResponse(String requestedConsumer) {
try {
ConsumerId consumer = getConsumerOrDefault(requestedConsumer, metricsConsumers);
var metricsByNode = metricsRetriever.getMetrics(consumer);
- return new JsonResponse(OK, toGenericApplicationModel(metricsByNode).serialize());
+ return new MetricsJsonResponse(OK, toGenericApplicationModel(metricsByNode)::serialize);
} catch (Exception e) {
log.log(Level.WARNING, "Got exception when retrieving metrics:", e);
@@ -75,7 +76,7 @@ public class ApplicationMetricsHandler extends HttpHandlerBase {
}
}
- private TextResponse applicationPrometheusResponse(String requestedConsumer) {
+ private HttpResponse applicationPrometheusResponse(String requestedConsumer) {
ConsumerId consumer = getConsumerOrDefault(requestedConsumer, metricsConsumers);
var metricsByNode = metricsRetriever.getMetrics(consumer);
@@ -87,7 +88,7 @@ public class ApplicationMetricsHandler extends HttpHandlerBase {
.map(builder -> builder.putDimension(DimensionId.toDimensionId("hostname"), element.hostname))
.map(MetricsPacket.Builder::build))
.toList();
- return new TextResponse(200, toPrometheusModel(metricsForAllNodes).serialize());
+ return new PrometheusResponse(200, toPrometheusModel(metricsForAllNodes));
}
}
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/metrics/MetricsV1Handler.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/metrics/MetricsV1Handler.java
index 3e4565c780b..50c1420edef 100644
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/metrics/MetricsV1Handler.java
+++ b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/metrics/MetricsV1Handler.java
@@ -3,6 +3,7 @@ package ai.vespa.metricsproxy.http.metrics;
import ai.vespa.metricsproxy.core.MetricsConsumers;
import ai.vespa.metricsproxy.core.MetricsManager;
+import ai.vespa.metricsproxy.http.MetricsJsonResponse;
import ai.vespa.metricsproxy.http.ValuesFetcher;
import ai.vespa.metricsproxy.metric.model.MetricsPacket;
import ai.vespa.metricsproxy.service.VespaServices;
@@ -51,10 +52,10 @@ public class MetricsV1Handler extends HttpHandlerBase {
return Optional.empty();
}
- private JsonResponse valuesResponse(String consumer) {
+ private HttpResponse valuesResponse(String consumer) {
try {
List<MetricsPacket> metrics = valuesFetcher.fetch(consumer);
- return new JsonResponse(OK, toGenericJsonModel(metrics).serialize());
+ return new MetricsJsonResponse(OK, toGenericJsonModel(metrics)::serialize);
} catch (Exception e) {
log.log(Level.WARNING, "Got exception when rendering metrics:", e);
return new ErrorResponse(INTERNAL_SERVER_ERROR, e.getMessage());
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/metrics/MetricsV2Handler.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/metrics/MetricsV2Handler.java
index 1f6cc17b2e1..7e9bba466df 100644
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/metrics/MetricsV2Handler.java
+++ b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/metrics/MetricsV2Handler.java
@@ -3,6 +3,7 @@ package ai.vespa.metricsproxy.http.metrics;
import ai.vespa.metricsproxy.core.MetricsConsumers;
import ai.vespa.metricsproxy.core.MetricsManager;
+import ai.vespa.metricsproxy.http.MetricsJsonResponse;
import ai.vespa.metricsproxy.http.ValuesFetcher;
import ai.vespa.metricsproxy.http.application.ClusterIdDimensionProcessor;
import ai.vespa.metricsproxy.http.application.Node;
@@ -62,7 +63,7 @@ public class MetricsV2Handler extends HttpHandlerBase {
return Optional.empty();
}
- private JsonResponse valuesResponse(String consumer) {
+ private HttpResponse valuesResponse(String consumer) {
try {
List<MetricsPacket> metrics = processAndBuild(valuesFetcher.fetchMetricsAsBuilders(consumer),
new ServiceIdDimensionProcessor(),
@@ -71,7 +72,7 @@ public class MetricsV2Handler extends HttpHandlerBase {
Node localNode = new Node(nodeInfoConfig.role(), nodeInfoConfig.hostname(), 0, "");
Map<Node, List<MetricsPacket>> metricsByNode = Map.of(localNode, metrics);
- return new JsonResponse(OK, toGenericApplicationModel(metricsByNode).serialize());
+ return new MetricsJsonResponse(OK, toGenericApplicationModel(metricsByNode)::serialize);
} catch (Exception e) {
log.log(Level.WARNING, "Got exception when rendering metrics:", e);
return new ErrorResponse(INTERNAL_SERVER_ERROR, e.getMessage());
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/prometheus/PrometheusHandler.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/prometheus/PrometheusHandler.java
index d73561b5eff..e609b54b916 100644
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/prometheus/PrometheusHandler.java
+++ b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/prometheus/PrometheusHandler.java
@@ -3,6 +3,7 @@ package ai.vespa.metricsproxy.http.prometheus;
import ai.vespa.metricsproxy.core.MetricsConsumers;
import ai.vespa.metricsproxy.core.MetricsManager;
+import ai.vespa.metricsproxy.http.PrometheusResponse;
import ai.vespa.metricsproxy.http.TextResponse;
import ai.vespa.metricsproxy.http.ValuesFetcher;
import ai.vespa.metricsproxy.metric.dimensions.ApplicationDimensions;
@@ -56,11 +57,11 @@ public class PrometheusHandler extends HttpHandlerBase {
return Optional.empty();
}
- private TextResponse valuesResponse(String consumer) {
+ private HttpResponse valuesResponse(String consumer) {
try {
List<MetricsPacket> metrics = new ArrayList<>(valuesFetcher.fetch(consumer));
metrics.addAll(nodeMetricGatherer.gatherMetrics());
- return new TextResponse(OK, toPrometheusModel(metrics).serialize());
+ return new PrometheusResponse(OK, toPrometheusModel(metrics));
} catch (Exception e) {
log.log(Level.WARNING, "Got exception when rendering metrics:", e);
return new TextResponse(INTERNAL_SERVER_ERROR, e.getMessage());
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
index 59d1f9e5c83..105724d17f8 100644
--- 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
@@ -5,10 +5,13 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import java.io.OutputStream;
import java.util.List;
import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_ABSENT;
+import static java.nio.charset.StandardCharsets.UTF_8;
/**
* @author gjoranv
@@ -21,8 +24,14 @@ public class GenericApplicationModel {
public List<GenericJsonModel> nodes;
public String serialize() {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ serialize(out);
+ return out.toString(UTF_8);
+ }
+
+ public void serialize(OutputStream out) {
try {
- return JacksonUtil.objectMapper().writeValueAsString(this);
+ JacksonUtil.objectMapper().writeValue(out, this);
} catch (IOException e) {
throw new JsonRenderingException("Could not render application nodes. Check the log for details.", e);
}
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/json/GenericJsonModel.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/json/GenericJsonModel.java
index c4ba0f39a18..54616dff759 100644
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/json/GenericJsonModel.java
+++ b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/json/GenericJsonModel.java
@@ -6,10 +6,13 @@ import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import java.io.OutputStream;
import java.util.List;
import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_ABSENT;
+import static java.nio.charset.StandardCharsets.UTF_8;
/**
* @author gjoranv
@@ -32,8 +35,14 @@ public class GenericJsonModel {
public List<GenericService> services;
public String serialize() {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ serialize(out);
+ return out.toString(UTF_8);
+ }
+
+ public void serialize(OutputStream out) {
try {
- return JacksonUtil.objectMapper().writeValueAsString(this);
+ JacksonUtil.objectMapper().writeValue(out, this);
} catch (IOException e) {
throw new JsonRenderingException("Could not render metrics. Check the log for details.", e);
}
diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/prometheus/PrometheusModel.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/prometheus/PrometheusModel.java
index b4d26069b6f..0f3878821f3 100644
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/prometheus/PrometheusModel.java
+++ b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/prometheus/PrometheusModel.java
@@ -11,6 +11,7 @@ import io.prometheus.client.Collector.MetricFamilySamples.Sample;
import io.prometheus.client.exporter.common.TextFormat;
import java.io.StringWriter;
+import java.io.Writer;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
@@ -27,8 +28,8 @@ public class PrometheusModel implements Enumeration<MetricFamilySamples> {
private final Iterator<MetricId> metricIterator;
private final Iterator<MetricFamilySamples> statusMetrics;
- PrometheusModel(Set<MetricId> metricNames, Map<ServiceId,
- List<MetricsPacket>> packetsByServiceId,
+ PrometheusModel(Set<MetricId> metricNames,
+ Map<ServiceId, List<MetricsPacket>> packetsByServiceId,
List<MetricFamilySamples> statusMetrics)
{
metricIterator = metricNames.iterator();
@@ -50,12 +51,16 @@ public class PrometheusModel implements Enumeration<MetricFamilySamples> {
public String serialize() {
var writer = new StringWriter();
+ serialize(writer);
+ return writer.toString();
+ }
+
+ public void serialize(Writer writer) {
try {
TextFormat.write004(writer, this);
} catch (Exception e) {
throw new PrometheusRenderingException("Could not render metrics. Check the log for details.", e);
}
- return writer.toString();
}
private MetricFamilySamples createMetricFamily(MetricId metricId) {
@@ -70,6 +75,7 @@ public class PrometheusModel implements Enumeration<MetricFamilySamples> {
}));
return new MetricFamilySamples(metricId.getIdForPrometheus(), Collector.Type.UNKNOWN, "", sampleList);
}
+
private static Sample createSample(ServiceId serviceId, MetricId metricId, Number metric,
Long timeStamp, Map<DimensionId, String> dimensions)
{
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
index 0e33d7dbf2f..052b8425a45 100644
--- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/MetricsParser.java
+++ b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/MetricsParser.java
@@ -53,8 +53,8 @@ public class MetricsParser {
throw new IOException("Expected start of object, got " + parser.currentToken());
}
- for (parser.nextToken(); parser.getCurrentToken() != JsonToken.END_OBJECT; parser.nextToken()) {
- String fieldName = parser.getCurrentName();
+ for (parser.nextToken(); parser.currentToken() != JsonToken.END_OBJECT; parser.nextToken()) {
+ String fieldName = parser.currentName();
JsonToken token = parser.nextToken();
if (fieldName.equals("metrics")) {
parseMetrics(parser, consumer);
@@ -67,12 +67,12 @@ public class MetricsParser {
}
static private Instant parseSnapshot(JsonParser parser) throws IOException {
- if (parser.getCurrentToken() != JsonToken.START_OBJECT) {
+ if (parser.currentToken() != JsonToken.START_OBJECT) {
throw new IOException("Expected start of 'snapshot' object, got " + parser.currentToken());
}
Instant timestamp = Instant.now();
- for (parser.nextToken(); parser.getCurrentToken() != JsonToken.END_OBJECT; parser.nextToken()) {
- String fieldName = parser.getCurrentName();
+ for (parser.nextToken(); parser.currentToken() != JsonToken.END_OBJECT; parser.nextToken()) {
+ String fieldName = parser.currentName();
JsonToken token = parser.nextToken();
if (fieldName.equals("to")) {
timestamp = Instant.ofEpochSecond(parser.getLongValue());
@@ -88,12 +88,12 @@ public class MetricsParser {
// 'metrics' object with 'snapshot' and 'values' arrays
static private void parseMetrics(JsonParser parser, Collector consumer) throws IOException {
- if (parser.getCurrentToken() != JsonToken.START_OBJECT) {
+ if (parser.currentToken() != JsonToken.START_OBJECT) {
throw new IOException("Expected start of 'metrics' object, got " + parser.currentToken());
}
Instant timestamp = Instant.now();
- for (parser.nextToken(); parser.getCurrentToken() != JsonToken.END_OBJECT; parser.nextToken()) {
- String fieldName = parser.getCurrentName();
+ for (parser.nextToken(); parser.currentToken() != JsonToken.END_OBJECT; parser.nextToken()) {
+ String fieldName = parser.currentName();
JsonToken token = parser.nextToken();
if (fieldName.equals("snapshot")) {
timestamp = parseSnapshot(parser);
@@ -109,7 +109,7 @@ public class MetricsParser {
// 'values' array
static private void parseMetricValues(JsonParser parser, Instant timestamp, Collector consumer) throws IOException {
- if (parser.getCurrentToken() != JsonToken.START_ARRAY) {
+ if (parser.currentToken() != JsonToken.START_ARRAY) {
throw new IOException("Expected start of 'metrics:values' array, got " + parser.currentToken());
}
@@ -126,8 +126,8 @@ public class MetricsParser {
String description = "";
Map<DimensionId, String> dim = Map.of();
List<Map.Entry<String, Number>> values = List.of();
- for (parser.nextToken(); parser.getCurrentToken() != JsonToken.END_OBJECT; parser.nextToken()) {
- String fieldName = parser.getCurrentName();
+ for (parser.nextToken(); parser.currentToken() != JsonToken.END_OBJECT; parser.nextToken()) {
+ String fieldName = parser.currentName();
JsonToken token = parser.nextToken();
switch (fieldName) {
case "name" -> name = parser.getText();
@@ -154,8 +154,8 @@ public class MetricsParser {
Set<Dimension> dimensions = new HashSet<>();
- for (parser.nextToken(); parser.getCurrentToken() != JsonToken.END_OBJECT; parser.nextToken()) {
- String fieldName = parser.getCurrentName();
+ for (parser.nextToken(); parser.currentToken() != JsonToken.END_OBJECT; parser.nextToken()) {
+ String fieldName = parser.currentName();
JsonToken token = parser.nextToken();
if (token == JsonToken.VALUE_STRING){
@@ -180,17 +180,16 @@ public class MetricsParser {
private static List<Map.Entry<String, Number>> parseValues(JsonParser parser) throws IOException {
List<Map.Entry<String, Number>> metrics = new ArrayList<>();
- for (parser.nextToken(); parser.getCurrentToken() != JsonToken.END_OBJECT; parser.nextToken()) {
- String fieldName = parser.getCurrentName();
+ for (parser.nextToken(); parser.currentToken() != JsonToken.END_OBJECT; parser.nextToken()) {
+ String metricName = parser.currentName();
JsonToken token = parser.nextToken();
- String metricName = fieldName;
if (token == JsonToken.VALUE_NUMBER_INT) {
metrics.add(Map.entry(metricName, parser.getLongValue()));
} else if (token == JsonToken.VALUE_NUMBER_FLOAT) {
double value = parser.getValueAsDouble();
metrics.add(Map.entry(metricName, value == ZERO_DOUBLE ? ZERO_DOUBLE : value));
} else {
- throw new IllegalArgumentException("Value for aggregator '" + fieldName + "' is not a number");
+ throw new IllegalArgumentException("Value for aggregator '" + metricName + "' is not a number");
}
}
return metrics;