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