All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ai.vespa.metricsproxy.metric.model.json.YamasJsonUtil Maven / Gradle / Ivy

There is a newer version: 8.441.21
Show newest version
// Copyright Vespa.ai. 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.metric.model.ConsumerId;
import ai.vespa.metricsproxy.metric.model.MetricsPacket;
import ai.vespa.metricsproxy.metric.model.ServiceId;
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 java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;
import java.util.stream.Collectors;

import static ai.vespa.metricsproxy.http.ValuesFetcher.defaultMetricsConsumerId;
import static ai.vespa.metricsproxy.metric.model.json.JacksonUtil.objectMapper;
import static java.util.logging.Level.WARNING;

/**
 * @author gjoranv
 */
public class YamasJsonUtil {

    private static final Logger log = Logger.getLogger(YamasJsonUtil.class.getName());
    private static final JsonFactory factory = JsonFactory.builder()
            .enable(StreamWriteFeature.WRITE_BIGDECIMAL_AS_PLAIN)
            .build();

    static final String YAMAS_ROUTING = "yamas";

    public static MetricsPacket.Builder toMetricsPacketBuilder(YamasJsonModel jsonModel) {
        if (jsonModel.application == null)
            throw new IllegalArgumentException("Service id cannot be null");

        return new MetricsPacket.Builder(ServiceId.toServiceId(jsonModel.application))
                .statusCode(jsonModel.status_code)
                .statusMessage(jsonModel.status_msg)
                .timestamp(Instant.ofEpochSecond(jsonModel.timestamp))
                .putMetrics(jsonModel.getMetricsList())
                .putDimensions(jsonModel.getDimensionsById())
                .addConsumers(jsonModel.getYamasConsumers());
    }

    /**
     * Converts the given json formatted string to a list of metrics packet builders.
     * Note that this method returns an empty list if an IOException occurs,
     * and logs a warning as a side effect.
     */
    public static List toMetricsPackets(String jsonString) {
        List packets = new ArrayList<>();
        var mapper = objectMapper();
        try (JsonParser jp = mapper.createParser(jsonString)) {
            while (jp.nextToken() != null) {
                YamasJsonModel jsonModel = jp.readValueAs(YamasJsonModel.class);
                packets.add(toMetricsPacketBuilder(jsonModel));
            }
            return packets;
        } catch (IOException e) {
            log.log(WARNING, "Could not create metrics packet from string:\n" + jsonString, e);
            return List.of();
        }
    }

    public static List appendOptionalStatusPacket(List packets) {
        if (packets.isEmpty()) return packets;

        Set consumers = extractSetForRouting(packets.get(0).consumers());
        if (consumers.isEmpty()) return packets;
        List withStatus = new ArrayList<>(packets);
        withStatus.add(new MetricsPacket.Builder(ServiceId.toServiceId("yms_check_vespa"))
                .statusCode(0)
                .statusMessage("Data collected successfully")
                .addConsumers(consumers).build());
        return withStatus;
    }

    public static String toJson(List metrics, boolean addStatus) {
        try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
            toJson(metrics, output, addStatus);
            output.flush();
            return output.toString();
        } catch (IOException e) {
            return "{}";
        }
    }
    private static Set extractSetForRouting(Set consumers) {
        return consumers.stream()
                .filter(consumerId -> consumerId != defaultMetricsConsumerId)
                .collect(Collectors.toSet());
    }
    public static void toJson(List metrics, OutputStream outputStream, boolean addStatus) throws IOException {
        JsonGenerator generator = factory.createGenerator(outputStream);
        generator.writeStartObject();
        if (metrics.isEmpty()) {
            generator.writeEndObject();
            return;
        }
        generator.writeArrayFieldStart("metrics");
        for (int i = 0; i < metrics.size() - 1; i++) {
            toJson(metrics.get(i), generator, addStatus);
        }
        toJson(metrics.get(metrics.size() - 1), generator, addStatus);
        generator.writeEndArray();
        generator.writeEndObject();
        generator.close();
    }

    public static void toJsonl(List 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) {
            generator.writeNumberField("status_code", metric.statusCode());
        }
        if ( ! Instant.EPOCH.equals(metric.timestamp())) {
            generator.writeNumberField("timestamp", metric.timestamp().getEpochSecond());
        }
        generator.writeStringField("application", metric.service().id);

        if ( ! metric.metrics().isEmpty()) {
            generator.writeObjectFieldStart("metrics");
            for (var m : metric.metrics().entrySet()) {
                generator.writeFieldName(m.getKey().id);
                JacksonUtil.writeDouble(generator, m.getValue().doubleValue());
            }
            generator.writeEndObject();
        }

        if ( ! metric.dimensions().isEmpty()) {
            generator.writeObjectFieldStart("dimensions");
            for (var m : metric.dimensions().entrySet()) {
                generator.writeStringField(m.getKey().id, m.getValue());
            }
            generator.writeEndObject();
        }
        Set routing = extractSetForRouting(metric.consumers());
        if (!routing.isEmpty()) {
            generator.writeObjectFieldStart("routing");
            generator.writeObjectFieldStart(YAMAS_ROUTING);
            generator.writeArrayFieldStart("namespaces");
            for (ConsumerId consumer : routing) {
                generator.writeString(consumer.id);
            }
            generator.writeEndArray();
            generator.writeEndObject();
            generator.writeEndObject();
        }
        if (addStatus) {
            generator.writeStringField("status_msg", metric.statusMessage());
        }
        generator.writeEndObject();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy