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