001package io.prometheus.metrics.instrumentation.dropwizard5.labels;
002
003import io.prometheus.metrics.model.snapshots.Labels;
004
005import java.util.ArrayList;
006import java.util.List;
007import java.util.Map;
008
009/**
010 * A LabelMapper to allow Dropwizard metrics to be translated to Prometheus metrics including custom labels and names.
011 * Prometheus metric name and labels are extracted from the Dropwizard name based on the provided list of {@link MapperConfig}s.
012 * The FIRST matching config will be used.
013 */
014public class CustomLabelMapper  {
015    private final List<CompiledMapperConfig> compiledMapperConfigs;
016
017    public CustomLabelMapper(final List<MapperConfig> mapperConfigs) {
018        if (mapperConfigs == null || mapperConfigs.isEmpty()) {
019            throw new IllegalArgumentException("CustomLabelMapper needs some mapper configs!");
020        }
021
022        this.compiledMapperConfigs = new ArrayList<CompiledMapperConfig>(mapperConfigs.size());
023        for (MapperConfig config : mapperConfigs) {
024            this.compiledMapperConfigs.add(new CompiledMapperConfig(config));
025        }
026    }
027
028    public String getName(final String dropwizardName){
029        if (dropwizardName == null) {
030            throw new IllegalArgumentException("Dropwizard metric name cannot be null");
031        }
032
033        CompiledMapperConfig matchingConfig = null;
034        for (CompiledMapperConfig config : this.compiledMapperConfigs) {
035            if (config.pattern.matches(dropwizardName)) {
036                matchingConfig = config;
037                break;
038            }
039        }
040
041        if (matchingConfig != null) {
042            final Map<String, String> params = matchingConfig.pattern.extractParameters(dropwizardName);
043            final NameAndLabels nameAndLabels = getNameAndLabels(matchingConfig.mapperConfig, params);
044            return nameAndLabels.name;
045        }
046
047        return dropwizardName;
048    }
049
050
051    public Labels getLabels(final String dropwizardName, final List<String> additionalLabelNames, final List<String> additionalLabelValues){
052        if (dropwizardName == null) {
053            throw new IllegalArgumentException("Dropwizard metric name cannot be null");
054        }
055
056        CompiledMapperConfig matchingConfig = null;
057        for (CompiledMapperConfig config : this.compiledMapperConfigs) {
058            if (config.pattern.matches(dropwizardName)) {
059                matchingConfig = config;
060                break;
061            }
062        }
063
064        if (matchingConfig != null) {
065            final Map<String, String> params = matchingConfig.pattern.extractParameters(dropwizardName);
066            final NameAndLabels nameAndLabels = getNameAndLabels(matchingConfig.mapperConfig, params);
067            nameAndLabels.labelNames.addAll(additionalLabelNames);
068            nameAndLabels.labelValues.addAll(additionalLabelValues);
069            return Labels.of(nameAndLabels.labelNames, nameAndLabels.labelValues);
070        }
071
072        return Labels.of(additionalLabelNames, additionalLabelValues);
073    }
074
075    protected NameAndLabels getNameAndLabels(final MapperConfig config, final Map<String, String> parameters) {
076        final String metricName = formatTemplate(config.getName(), parameters);
077        final List<String> labels = new ArrayList<String>(config.getLabels().size());
078        final List<String> labelValues = new ArrayList<String>(config.getLabels().size());
079        for (Map.Entry<String, String> entry : config.getLabels().entrySet()) {
080            labels.add(entry.getKey());
081            labelValues.add(formatTemplate(entry.getValue(), parameters));
082        }
083
084        return new NameAndLabels(metricName, labels, labelValues);
085    }
086
087    private String formatTemplate(final String template, final Map<String, String> params) {
088        String result = template;
089        for (Map.Entry<String, String> entry : params.entrySet()) {
090            result = result.replace(entry.getKey(), entry.getValue());
091        }
092
093        return result;
094    }
095
096    static class CompiledMapperConfig {
097        final MapperConfig mapperConfig;
098        final GraphiteNamePattern pattern;
099
100        CompiledMapperConfig(final MapperConfig mapperConfig) {
101            this.mapperConfig = mapperConfig;
102            this.pattern = new GraphiteNamePattern(mapperConfig.getMatch());
103        }
104    }
105
106    static class NameAndLabels {
107        final String name;
108        final List<String> labelNames;
109        final List<String> labelValues;
110
111        NameAndLabels(final String name, final List<String> labelNames, final List<String> labelValues) {
112            this.name = name;
113            this.labelNames = labelNames;
114            this.labelValues = labelValues;
115        }
116    }
117}