aboutsummaryrefslogtreecommitdiffstats
path: root/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/json/GenericJsonUtil.java
blob: a71a5526fc25991a7858c1eab5760a0b282747aa (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
// Copyright Yahoo. 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 java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import java.util.stream.Collectors;

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.json.JacksonUtil.createObjectMapper;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static java.util.logging.Level.WARNING;
import static java.util.stream.Collectors.toList;

/**
 * Utilities for converting between metrics packets and the generic json format.
 *
 * @author gjoranv
 */
public class GenericJsonUtil {
    private static final Logger log = Logger.getLogger(GenericJsonUtil.class.getName());

    private GenericJsonUtil() {
    }

    public static GenericApplicationModel toGenericApplicationModel(Map<Node, List<MetricsPacket>> metricsByNode) {
        var applicationModel = new GenericApplicationModel();

        var genericJsonModels = new ArrayList<GenericJsonModel>();
        metricsByNode.forEach(
                (node, metrics) -> genericJsonModels.add(toGenericJsonModel(metrics, node)));

        applicationModel.nodes = genericJsonModels;
        return applicationModel;
    }

    public static GenericJsonModel toGenericJsonModel(List<MetricsPacket> metricsPackets) {
        return toGenericJsonModel(metricsPackets, null);
    }

    public static GenericJsonModel toGenericJsonModel(List<MetricsPacket> metricsPackets, Node node) {
        Map<ServiceId, List<MetricsPacket>> packetsByService = metricsPackets.stream()
                .collect(Collectors.groupingBy(packet -> packet.service, LinkedHashMap::new, toList()));

        var jsonModel = new GenericJsonModel();
        if (node != null) {
            jsonModel.hostname = node.hostname;
            jsonModel.role = node.role;
        }

        var genericServices = new ArrayList<GenericService>();
        packetsByService.forEach((serviceId, packets) -> {
            var genericMetricsList = packets.stream()
                    .filter(packet -> ! (packet.metrics().isEmpty() && packet.dimensions().isEmpty()))
                    .map(packet -> new GenericMetrics(packet.metrics(), packet.dimensions()))
                    .toList();
            var genericService = packets.stream().findFirst()
                    .map(firstPacket -> new GenericService(serviceId.id,
                                                           firstPacket.timestamp,
                                                           StatusCode.values()[firstPacket.statusCode],
                                                           firstPacket.statusMessage,
                                                           genericMetricsList))
                    .get();
            if (VESPA_NODE_SERVICE_ID.equals(serviceId)) {
                jsonModel.node = new GenericNode(genericService.timestamp, genericService.metrics);
            } else {
                genericServices.add(genericService);

            }
        });

        jsonModel.services = genericServices;
        return jsonModel;
    }

    public static List<MetricsPacket.Builder> toMetricsPackets(String jsonString) {
        try {
            ObjectMapper mapper = createObjectMapper();
            GenericJsonModel jsonModel = mapper.readValue(jsonString, GenericJsonModel.class);
            return toMetricsPackets(jsonModel);
        } catch (IOException e) {
            log.log(WARNING, "Could not create metrics packet from string:\n" + jsonString, e);
            return emptyList();
        }
    }

    public static List<MetricsPacket.Builder> toMetricsPackets(GenericJsonModel jsonModel) {
        var packets = toNodePackets(jsonModel.node);
        jsonModel.services.forEach(genericService -> packets.addAll(toServicePackets(genericService)));

        return packets;
    }

    private static List<MetricsPacket.Builder> toNodePackets(GenericNode node) {
        List<MetricsPacket.Builder> packets = new ArrayList<>();
        if (node == null) return packets;

        if (node.metrics == null || node.metrics.isEmpty()) {
            return singletonList(new MetricsPacket.Builder(VESPA_NODE_SERVICE_ID)
                                         .statusCode(StatusCode.UP.ordinal())
                                         .timestamp(node.timestamp));
        }

        for (var genericMetrics : node.metrics) {
            var packet = new MetricsPacket.Builder(VESPA_NODE_SERVICE_ID)
                    .statusCode(StatusCode.UP.ordinal())
                    .timestamp(node.timestamp);
            addMetrics(genericMetrics, packet);
            packets.add(packet);
        }
        return packets;
    }

    private static List<MetricsPacket.Builder> toServicePackets(GenericService service) {
        List<MetricsPacket.Builder> packets = new ArrayList<>();
        if (service.metrics == null || service.metrics.isEmpty())
            return singletonList(newServicePacket(service));

        for (var genericMetrics : service.metrics) {
            var packet = newServicePacket(service);
            addMetrics(genericMetrics, packet);
            packets.add(packet);
        }
        return packets;

    }

    private static MetricsPacket.Builder newServicePacket(GenericService service) {
        return new MetricsPacket.Builder(ServiceId.toServiceId(service.name))
                .statusCode(StatusCode.fromString(service.status.code).ordinal())
                .statusMessage(service.status.description)
                .timestamp(service.timestamp);
    }

    private static void addMetrics(GenericMetrics genericMetrics, MetricsPacket.Builder packet) {
        genericMetrics.values.forEach((id, value) -> packet.putMetric(toMetricId(id), value));
        genericMetrics.dimensions.forEach((id, value) -> packet.putDimension(toDimensionId(id), value));
    }

}