001package io.prometheus.metrics.config; 002 003import java.util.HashMap; 004import java.util.Map; 005import java.util.function.Consumer; 006import javax.annotation.Nullable; 007 008/** 009 * The Prometheus Java client library can be configured at runtime (e.g. using a properties file). 010 * 011 * <p>This class represents the runtime configuration. 012 */ 013public class PrometheusProperties { 014 015 private static final PrometheusProperties instance = PrometheusPropertiesLoader.load(); 016 017 private final MetricsProperties defaultMetricsProperties; 018 private final MetricPropertiesMap metricProperties; 019 private final ExemplarsProperties exemplarProperties; 020 private final ExporterProperties exporterProperties; 021 private final ExporterFilterProperties exporterFilterProperties; 022 private final ExporterHttpServerProperties exporterHttpServerProperties; 023 private final ExporterOpenTelemetryProperties exporterOpenTelemetryProperties; 024 private final ExporterPushgatewayProperties exporterPushgatewayProperties; 025 private final OpenMetrics2Properties openMetrics2Properties; 026 027 /** 028 * Map that stores metric-specific properties keyed by metric name in exposition format 029 * (underscores instead of dots). 030 * 031 * <p>This wrapper makes it explicit that metric names are normalized to underscore format for 032 * storage, so that environment variables and properties with dots in metric names can be 033 * correctly looked up using normalized names. 034 */ 035 static class MetricPropertiesMap { 036 private final Map<String, MetricsProperties> map = new HashMap<>(); 037 038 void set(Map<String, MetricsProperties> properties) { 039 map.clear(); 040 properties.forEach(this::put); 041 } 042 043 void put(String metricName, MetricsProperties properties) { 044 map.put(normalize(metricName), properties); 045 } 046 047 /** 048 * Get metric properties by metric name. 049 * 050 * <p>Accepts metric names in any format (with dots or underscores) and automatically converts 051 * them to the normalized underscore format used for storage. 052 * 053 * @param metricName the metric name (dots will be converted to underscores) 054 * @return the metric properties, or null if not configured 055 */ 056 @Nullable 057 MetricsProperties get(String metricName) { 058 return map.get(normalize(metricName)); 059 } 060 061 // copied from PrometheusNaming - but we can't reuse that class here because it's in a module 062 // that 063 // depends on PrometheusProperties, which would create a circular dependency. 064 private static String normalize(String name) { 065 StringBuilder escaped = new StringBuilder(); 066 067 for (int i = 0; i < name.length(); ) { 068 int c = name.codePointAt(i); 069 if (isValidLegacyChar(c, i)) { 070 escaped.appendCodePoint(c); 071 } else { 072 escaped.append('_'); 073 } 074 i += Character.charCount(c); 075 } 076 return escaped.toString(); 077 } 078 } 079 080 private static boolean isValidLegacyChar(int c, int i) { 081 return (c >= 'a' && c <= 'z') 082 || (c >= 'A' && c <= 'Z') 083 || c == '_' 084 || c == ':' 085 || (c >= '0' && c <= '9' && i > 0); 086 } 087 088 /** 089 * Get the properties instance. When called for the first time, {@code get()} loads the properties 090 * from the following locations: 091 * 092 * <ul> 093 * <li>{@code prometheus.properties} file found in the classpath. 094 * <li>Properties file specified in the {@code PROMETHEUS_CONFIG} environment variable or the 095 * {@code prometheus.config} system property. 096 * <li>Individual properties from system properties. 097 * </ul> 098 */ 099 public static PrometheusProperties get() throws PrometheusPropertiesException { 100 return instance; 101 } 102 103 public static Builder builder() { 104 return new Builder(); 105 } 106 107 // Package-private constructor for PrometheusPropertiesLoader and Builder 108 PrometheusProperties( 109 MetricsProperties defaultMetricsProperties, 110 MetricPropertiesMap metricProperties, 111 ExemplarsProperties exemplarProperties, 112 ExporterProperties exporterProperties, 113 ExporterFilterProperties exporterFilterProperties, 114 ExporterHttpServerProperties httpServerConfig, 115 ExporterPushgatewayProperties pushgatewayProperties, 116 ExporterOpenTelemetryProperties otelConfig, 117 OpenMetrics2Properties openMetrics2Properties) { 118 this.defaultMetricsProperties = defaultMetricsProperties; 119 this.metricProperties = metricProperties; 120 this.exemplarProperties = exemplarProperties; 121 this.exporterProperties = exporterProperties; 122 this.exporterFilterProperties = exporterFilterProperties; 123 this.exporterHttpServerProperties = httpServerConfig; 124 this.exporterPushgatewayProperties = pushgatewayProperties; 125 this.exporterOpenTelemetryProperties = otelConfig; 126 this.openMetrics2Properties = openMetrics2Properties; 127 } 128 129 /** 130 * The default metric properties apply for metrics where {@link #getMetricProperties(String)} is 131 * {@code null}. 132 */ 133 public MetricsProperties getDefaultMetricProperties() { 134 return defaultMetricsProperties; 135 } 136 137 /** 138 * Properties specific for one metric. Should be merged with {@link 139 * #getDefaultMetricProperties()}. May return {@code null} if no metric-specific properties are 140 * configured for a metric name. 141 * 142 * @param metricName the metric name (dots will be automatically converted to underscores to match 143 * exposition format) 144 */ 145 @Nullable 146 public MetricsProperties getMetricProperties(String metricName) { 147 return metricProperties.get(metricName); 148 } 149 150 public ExemplarsProperties getExemplarProperties() { 151 return exemplarProperties; 152 } 153 154 public ExporterProperties getExporterProperties() { 155 return exporterProperties; 156 } 157 158 public ExporterFilterProperties getExporterFilterProperties() { 159 return exporterFilterProperties; 160 } 161 162 public ExporterHttpServerProperties getExporterHttpServerProperties() { 163 return exporterHttpServerProperties; 164 } 165 166 public ExporterPushgatewayProperties getExporterPushgatewayProperties() { 167 return exporterPushgatewayProperties; 168 } 169 170 public ExporterOpenTelemetryProperties getExporterOpenTelemetryProperties() { 171 return exporterOpenTelemetryProperties; 172 } 173 174 public OpenMetrics2Properties getOpenMetrics2Properties() { 175 return openMetrics2Properties; 176 } 177 178 public static class Builder { 179 private MetricsProperties defaultMetricsProperties = MetricsProperties.builder().build(); 180 private final MetricPropertiesMap metricProperties = new MetricPropertiesMap(); 181 private ExemplarsProperties exemplarProperties = ExemplarsProperties.builder().build(); 182 private ExporterProperties exporterProperties = ExporterProperties.builder().build(); 183 private ExporterFilterProperties exporterFilterProperties = 184 ExporterFilterProperties.builder().build(); 185 private ExporterHttpServerProperties exporterHttpServerProperties = 186 ExporterHttpServerProperties.builder().build(); 187 private ExporterPushgatewayProperties pushgatewayProperties = 188 ExporterPushgatewayProperties.builder().build(); 189 private ExporterOpenTelemetryProperties otelConfig = 190 ExporterOpenTelemetryProperties.builder().build(); 191 private OpenMetrics2Properties openMetrics2Properties = 192 OpenMetrics2Properties.builder().build(); 193 194 private Builder() {} 195 196 public Builder defaultMetricsProperties(MetricsProperties defaultMetricsProperties) { 197 this.defaultMetricsProperties = defaultMetricsProperties; 198 return this; 199 } 200 201 public Builder metricProperties(Map<String, MetricsProperties> metricProperties) { 202 this.metricProperties.set(metricProperties); 203 return this; 204 } 205 206 /** Convenience for adding a single named MetricsProperties */ 207 public Builder putMetricProperty(String name, MetricsProperties props) { 208 this.metricProperties.put(name, props); 209 return this; 210 } 211 212 public Builder exemplarProperties(ExemplarsProperties exemplarProperties) { 213 this.exemplarProperties = exemplarProperties; 214 return this; 215 } 216 217 public Builder exporterProperties(ExporterProperties exporterProperties) { 218 this.exporterProperties = exporterProperties; 219 return this; 220 } 221 222 public Builder exporterFilterProperties(ExporterFilterProperties exporterFilterProperties) { 223 this.exporterFilterProperties = exporterFilterProperties; 224 return this; 225 } 226 227 public Builder exporterHttpServerProperties( 228 ExporterHttpServerProperties exporterHttpServerProperties) { 229 this.exporterHttpServerProperties = exporterHttpServerProperties; 230 return this; 231 } 232 233 public Builder pushgatewayProperties(ExporterPushgatewayProperties pushgatewayProperties) { 234 this.pushgatewayProperties = pushgatewayProperties; 235 return this; 236 } 237 238 public Builder exporterOpenTelemetryProperties( 239 ExporterOpenTelemetryProperties exporterOpenTelemetryProperties) { 240 this.otelConfig = exporterOpenTelemetryProperties; 241 return this; 242 } 243 244 public Builder enableOpenMetrics2(Consumer<OpenMetrics2Properties.Builder> configurator) { 245 OpenMetrics2Properties.Builder openMetrics2Builder = OpenMetrics2Properties.builder(); 246 configurator.accept(openMetrics2Builder); 247 this.openMetrics2Properties = openMetrics2Builder.build(); 248 return this; 249 } 250 251 public Builder openMetrics2Properties(OpenMetrics2Properties openMetrics2Properties) { 252 this.openMetrics2Properties = openMetrics2Properties; 253 return this; 254 } 255 256 public PrometheusProperties build() { 257 return new PrometheusProperties( 258 defaultMetricsProperties, 259 metricProperties, 260 exemplarProperties, 261 exporterProperties, 262 exporterFilterProperties, 263 exporterHttpServerProperties, 264 pushgatewayProperties, 265 otelConfig, 266 openMetrics2Properties); 267 } 268 } 269}