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