aboutsummaryrefslogtreecommitdiffstats
path: root/config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/ConsumersConfigGenerator.java
blob: ea3d5e55b07bd737366543b872df3327e97d465c (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
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

package com.yahoo.vespa.model.admin.metricsproxy;

import ai.vespa.metrics.set.Metric;
import ai.vespa.metrics.set.MetricSet;
import ai.vespa.metricsproxy.core.ConsumersConfig.Consumer;
import com.yahoo.config.provision.SystemName;
import com.yahoo.vespa.model.admin.monitoring.MetricsConsumer;

import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * Helper class to generate config for metrics consumers.
 *
 * @author gjoranv
 */
class ConsumersConfigGenerator {

    /**
     * @param userConsumers the consumers set up by the user in services.xml
     * @return a list of consumer builders (a mapping from consumer to its metrics)
     */
    static List<Consumer.Builder> generateConsumers(MetricsConsumer defaultConsumer,
                                                    Map<String, MetricsConsumer> userConsumers,
                                                    SystemName systemName) {
        // Normally, the user given consumers should not contain VESPA_CONSUMER_ID,
        // but it's allowed for some internally used applications.
        var allConsumers = new LinkedHashMap<>(userConsumers);
        allConsumers.put(MetricsConsumer.vespa.id(),
                         combineConsumers(defaultConsumer, allConsumers.get(MetricsConsumer.vespa.id())));
        allConsumers.put(MetricsConsumer.autoscaling.id(), MetricsConsumer.autoscaling);

        if (systemName.isPublic())
            allConsumers.put(MetricsConsumer.vespaCloud.id(), MetricsConsumer.vespaCloud);

        return allConsumers.values().stream()
                .map(ConsumersConfigGenerator::toConsumerBuilder)
                .toList();
    }

    /*
     * Returns a new consumer that is a combination of the two given consumers
     * (ignoring the id of the consumers' metric sets).
     * If a metric with the same id exists in both consumers, output name and
     * dimensions from the 'overriding' consumer is used, but dimensions from 'original'
     * are added if they don't exist in 'overriding'.
     */
    private static MetricsConsumer combineConsumers(MetricsConsumer original, MetricsConsumer overriding) {
        if (overriding == null) return original;
        return addMetrics(original, overriding.metrics());
    }

    static MetricsConsumer addMetrics(MetricsConsumer original, Map<String, Metric> metrics) {
        if (metrics == null) return original;

        Map<String, Metric> combinedMetrics = new LinkedHashMap<>(original.metrics());
        metrics.forEach((name, newMetric) ->
                                combinedMetrics.put(name, combineMetrics(original.metrics().get(name), newMetric)));

        return new MetricsConsumer(original.id(),
                                   new MetricSet(original.metricSet().getId(), combinedMetrics.values()));
    }

    private static Metric combineMetrics(Metric original, Metric newMetric) {
        return original != null ? newMetric.addDimensionsFrom(original) : newMetric;
    }

    static Consumer.Builder toConsumerBuilder(MetricsConsumer consumer) {
        Consumer.Builder builder = new Consumer.Builder().name(consumer.id());
        consumer.metrics().values().stream().sorted(Comparator.comparing(a -> a.name)).forEach(metric -> builder.metric(toConsumerMetricBuilder(metric)));
        return builder;
    }

    private static Consumer.Metric.Builder toConsumerMetricBuilder(Metric metric) {
        Consumer.Metric.Builder builder = new Consumer.Metric.Builder().name(metric.name)
                .outputname(metric.outputName)
                .description(metric.description);
        metric.dimensions.forEach((name, value) -> builder.dimension(toMetricDimensionBuilder(name, value)));
        return builder;
    }

    private static Consumer.Metric.Dimension.Builder toMetricDimensionBuilder(String name, String value) {
        return new Consumer.Metric.Dimension.Builder()
                .key(name)
                .value(value);
    }

}