001package io.prometheus.metrics.config; 002 003import static java.util.Collections.unmodifiableList; 004 005import io.prometheus.metrics.annotations.StableApi; 006import java.util.ArrayList; 007import java.util.Collections; 008import java.util.List; 009import javax.annotation.Nullable; 010 011/** Properties starting with io.prometheus.metrics */ 012@StableApi 013public class MetricsProperties { 014 015 private static final String EXEMPLARS_ENABLED = "exemplars_enabled"; 016 private static final String HISTOGRAM_NATIVE_ONLY = "histogram_native_only"; 017 private static final String HISTOGRAM_CLASSIC_ONLY = "histogram_classic_only"; 018 private static final String HISTOGRAM_CLASSIC_UPPER_BOUNDS = "histogram_classic_upper_bounds"; 019 private static final String HISTOGRAM_NATIVE_INITIAL_SCHEMA = "histogram_native_initial_schema"; 020 private static final String HISTOGRAM_NATIVE_MIN_ZERO_THRESHOLD = 021 "histogram_native_min_zero_threshold"; 022 private static final String HISTOGRAM_NATIVE_MAX_ZERO_THRESHOLD = 023 "histogram_native_max_zero_threshold"; 024 private static final String HISTOGRAM_NATIVE_MAX_NUMBER_OF_BUCKETS = 025 "histogram_native_max_number_of_buckets"; // 0 means unlimited number of buckets 026 private static final String HISTOGRAM_NATIVE_RESET_DURATION_SECONDS = 027 "histogram_native_reset_duration_seconds"; // 0 means no reset 028 private static final String SUMMARY_QUANTILES = "summary_quantiles"; 029 private static final String SUMMARY_QUANTILE_ERRORS = "summary_quantile_errors"; 030 private static final String SUMMARY_MAX_AGE_SECONDS = "summary_max_age_seconds"; 031 private static final String SUMMARY_NUMBER_OF_AGE_BUCKETS = "summary_number_of_age_buckets"; 032 033 /** 034 * All known property suffixes that can be configured for metrics. 035 * 036 * <p>This list is used to parse metric-specific configuration keys from environment variables. 037 */ 038 static final String[] PROPERTY_SUFFIXES = { 039 EXEMPLARS_ENABLED, 040 HISTOGRAM_NATIVE_ONLY, 041 HISTOGRAM_CLASSIC_ONLY, 042 HISTOGRAM_CLASSIC_UPPER_BOUNDS, 043 HISTOGRAM_NATIVE_INITIAL_SCHEMA, 044 HISTOGRAM_NATIVE_MIN_ZERO_THRESHOLD, 045 HISTOGRAM_NATIVE_MAX_ZERO_THRESHOLD, 046 HISTOGRAM_NATIVE_MAX_NUMBER_OF_BUCKETS, 047 HISTOGRAM_NATIVE_RESET_DURATION_SECONDS, 048 SUMMARY_QUANTILES, 049 SUMMARY_QUANTILE_ERRORS, 050 SUMMARY_MAX_AGE_SECONDS, 051 SUMMARY_NUMBER_OF_AGE_BUCKETS 052 }; 053 054 @Nullable private final Boolean exemplarsEnabled; 055 @Nullable private final Boolean histogramNativeOnly; 056 @Nullable private final Boolean histogramClassicOnly; 057 @Nullable private final List<Double> histogramClassicUpperBounds; 058 @Nullable private final Integer histogramNativeInitialSchema; 059 @Nullable private final Double histogramNativeMinZeroThreshold; 060 @Nullable private final Double histogramNativeMaxZeroThreshold; 061 @Nullable private final Integer histogramNativeMaxNumberOfBuckets; 062 @Nullable private final Long histogramNativeResetDurationSeconds; 063 @Nullable private final List<Double> summaryQuantiles; 064 @Nullable private final List<Double> summaryQuantileErrors; 065 @Nullable private final Long summaryMaxAgeSeconds; 066 @Nullable private final Integer summaryNumberOfAgeBuckets; 067 068 public MetricsProperties( 069 @Nullable Boolean exemplarsEnabled, 070 @Nullable Boolean histogramNativeOnly, 071 @Nullable Boolean histogramClassicOnly, 072 @Nullable List<Double> histogramClassicUpperBounds, 073 @Nullable Integer histogramNativeInitialSchema, 074 @Nullable Double histogramNativeMinZeroThreshold, 075 @Nullable Double histogramNativeMaxZeroThreshold, 076 @Nullable Integer histogramNativeMaxNumberOfBuckets, 077 @Nullable Long histogramNativeResetDurationSeconds, 078 @Nullable List<Double> summaryQuantiles, 079 @Nullable List<Double> summaryQuantileErrors, 080 @Nullable Long summaryMaxAgeSeconds, 081 @Nullable Integer summaryNumberOfAgeBuckets) { 082 this( 083 exemplarsEnabled, 084 histogramNativeOnly, 085 histogramClassicOnly, 086 histogramClassicUpperBounds, 087 histogramNativeInitialSchema, 088 histogramNativeMinZeroThreshold, 089 histogramNativeMaxZeroThreshold, 090 histogramNativeMaxNumberOfBuckets, 091 histogramNativeResetDurationSeconds, 092 summaryQuantiles, 093 summaryQuantileErrors, 094 summaryMaxAgeSeconds, 095 summaryNumberOfAgeBuckets, 096 ""); 097 } 098 099 private MetricsProperties( 100 @Nullable Boolean exemplarsEnabled, 101 @Nullable Boolean histogramNativeOnly, 102 @Nullable Boolean histogramClassicOnly, 103 @Nullable List<Double> histogramClassicUpperBounds, 104 @Nullable Integer histogramNativeInitialSchema, 105 @Nullable Double histogramNativeMinZeroThreshold, 106 @Nullable Double histogramNativeMaxZeroThreshold, 107 @Nullable Integer histogramNativeMaxNumberOfBuckets, 108 @Nullable Long histogramNativeResetDurationSeconds, 109 @Nullable List<Double> summaryQuantiles, 110 @Nullable List<Double> summaryQuantileErrors, 111 @Nullable Long summaryMaxAgeSeconds, 112 @Nullable Integer summaryNumberOfAgeBuckets, 113 String configPropertyPrefix) { 114 this.exemplarsEnabled = exemplarsEnabled; 115 this.histogramNativeOnly = isHistogramNativeOnly(histogramClassicOnly, histogramNativeOnly); 116 this.histogramClassicOnly = isHistogramClassicOnly(histogramClassicOnly, histogramNativeOnly); 117 this.histogramClassicUpperBounds = 118 histogramClassicUpperBounds == null 119 ? null 120 : unmodifiableList(new ArrayList<>(histogramClassicUpperBounds)); 121 this.histogramNativeInitialSchema = histogramNativeInitialSchema; 122 this.histogramNativeMinZeroThreshold = histogramNativeMinZeroThreshold; 123 this.histogramNativeMaxZeroThreshold = histogramNativeMaxZeroThreshold; 124 this.histogramNativeMaxNumberOfBuckets = histogramNativeMaxNumberOfBuckets; 125 this.histogramNativeResetDurationSeconds = histogramNativeResetDurationSeconds; 126 this.summaryQuantiles = 127 summaryQuantiles == null ? null : unmodifiableList(new ArrayList<>(summaryQuantiles)); 128 this.summaryQuantileErrors = 129 summaryQuantileErrors == null 130 ? null 131 : unmodifiableList(new ArrayList<>(summaryQuantileErrors)); 132 this.summaryMaxAgeSeconds = summaryMaxAgeSeconds; 133 this.summaryNumberOfAgeBuckets = summaryNumberOfAgeBuckets; 134 validate(configPropertyPrefix); 135 } 136 137 @Nullable 138 private Boolean isHistogramClassicOnly( 139 @Nullable Boolean histogramClassicOnly, @Nullable Boolean histogramNativeOnly) { 140 if (histogramClassicOnly != null) { 141 return histogramClassicOnly; 142 } 143 if (histogramNativeOnly != null) { 144 return !histogramNativeOnly; 145 } 146 return null; 147 } 148 149 @Nullable 150 private Boolean isHistogramNativeOnly( 151 @Nullable Boolean histogramClassicOnly, @Nullable Boolean histogramNativeOnly) { 152 if (histogramNativeOnly != null) { 153 return histogramNativeOnly; 154 } 155 if (histogramClassicOnly != null) { 156 return !histogramClassicOnly; 157 } 158 return null; 159 } 160 161 private void validate(String prefix) throws PrometheusPropertiesException { 162 Util.assertValue( 163 histogramNativeInitialSchema, 164 s -> s >= -4 && s <= 8, 165 "Expecting number between -4 and +8.", 166 prefix, 167 HISTOGRAM_NATIVE_INITIAL_SCHEMA); 168 Util.assertValue( 169 histogramNativeMinZeroThreshold, 170 t -> t >= 0, 171 "Expecting value >= 0.", 172 prefix, 173 HISTOGRAM_NATIVE_MIN_ZERO_THRESHOLD); 174 Util.assertValue( 175 histogramNativeMaxZeroThreshold, 176 t -> t >= 0, 177 "Expecting value >= 0.", 178 prefix, 179 HISTOGRAM_NATIVE_MAX_ZERO_THRESHOLD); 180 Util.assertValue( 181 histogramNativeMaxNumberOfBuckets, 182 n -> n >= 0, 183 "Expecting value >= 0.", 184 prefix, 185 HISTOGRAM_NATIVE_MAX_NUMBER_OF_BUCKETS); 186 Util.assertValue( 187 histogramNativeResetDurationSeconds, 188 t -> t >= 0, 189 "Expecting value >= 0.", 190 prefix, 191 HISTOGRAM_NATIVE_RESET_DURATION_SECONDS); 192 Util.assertValue( 193 summaryMaxAgeSeconds, t -> t > 0, "Expecting value > 0.", prefix, SUMMARY_MAX_AGE_SECONDS); 194 Util.assertValue( 195 summaryNumberOfAgeBuckets, 196 t -> t > 0, 197 "Expecting value > 0.", 198 prefix, 199 SUMMARY_NUMBER_OF_AGE_BUCKETS); 200 201 if (Boolean.TRUE.equals(histogramNativeOnly) && Boolean.TRUE.equals(histogramClassicOnly)) { 202 throw new PrometheusPropertiesException( 203 prefix 204 + "." 205 + HISTOGRAM_NATIVE_ONLY 206 + " and " 207 + prefix 208 + "." 209 + HISTOGRAM_CLASSIC_ONLY 210 + " cannot both be true"); 211 } 212 213 if (histogramNativeMinZeroThreshold != null && histogramNativeMaxZeroThreshold != null) { 214 if (histogramNativeMinZeroThreshold > histogramNativeMaxZeroThreshold) { 215 throw new PrometheusPropertiesException( 216 prefix 217 + "." 218 + HISTOGRAM_NATIVE_MIN_ZERO_THRESHOLD 219 + " cannot be greater than " 220 + prefix 221 + "." 222 + HISTOGRAM_NATIVE_MAX_ZERO_THRESHOLD); 223 } 224 } 225 226 if (summaryQuantiles != null) { 227 for (double quantile : summaryQuantiles) { 228 if (quantile < 0 || quantile > 1) { 229 throw new PrometheusPropertiesException( 230 prefix 231 + "." 232 + SUMMARY_QUANTILES 233 + ": Expecting 0.0 <= quantile <= 1.0. Found: " 234 + quantile); 235 } 236 } 237 } 238 239 if (summaryQuantileErrors != null) { 240 if (summaryQuantiles == null) { 241 throw new PrometheusPropertiesException( 242 prefix 243 + "." 244 + SUMMARY_QUANTILE_ERRORS 245 + ": Can't configure " 246 + SUMMARY_QUANTILE_ERRORS 247 + " without configuring " 248 + SUMMARY_QUANTILES); 249 } 250 if (summaryQuantileErrors.size() != summaryQuantiles.size()) { 251 String fullKey = 252 prefix.isEmpty() ? SUMMARY_QUANTILE_ERRORS : prefix + "." + SUMMARY_QUANTILE_ERRORS; 253 throw new PrometheusPropertiesException( 254 fullKey + ": must have the same length as " + SUMMARY_QUANTILES); 255 } 256 for (double error : summaryQuantileErrors) { 257 if (error < 0 || error > 1) { 258 String fullKey = 259 prefix.isEmpty() ? SUMMARY_QUANTILE_ERRORS : prefix + "." + SUMMARY_QUANTILE_ERRORS; 260 throw new PrometheusPropertiesException(fullKey + ": Expecting 0.0 <= error <= 1.0"); 261 } 262 } 263 } 264 } 265 266 /** 267 * This is the only configuration property that can be applied to all metric types. You can use it 268 * to turn Exemplar support off. Default is {@code true}. 269 */ 270 @Nullable 271 public Boolean getExemplarsEnabled() { 272 return exemplarsEnabled; 273 } 274 275 /** See {@code Histogram.Builder.nativeOnly()} */ 276 @Nullable 277 public Boolean getHistogramNativeOnly() { 278 return histogramNativeOnly; 279 } 280 281 /** See {@code Histogram.Builder.classicOnly()} */ 282 @Nullable 283 public Boolean getHistogramClassicOnly() { 284 return histogramClassicOnly; 285 } 286 287 /** See {@code Histogram.Builder.classicUpperBounds()} */ 288 @Nullable 289 public List<Double> getHistogramClassicUpperBounds() { 290 return histogramClassicUpperBounds; 291 } 292 293 /** See {@code Histogram.Builder.nativeInitialSchema()} */ 294 @Nullable 295 public Integer getHistogramNativeInitialSchema() { 296 return histogramNativeInitialSchema; 297 } 298 299 /** See {@code Histogram.Builder.nativeMinZeroThreshold()} */ 300 @Nullable 301 public Double getHistogramNativeMinZeroThreshold() { 302 return histogramNativeMinZeroThreshold; 303 } 304 305 /** See {@code Histogram.Builder.nativeMaxZeroThreshold()} */ 306 @Nullable 307 public Double getHistogramNativeMaxZeroThreshold() { 308 return histogramNativeMaxZeroThreshold; 309 } 310 311 /** See {@code Histogram.Builder.nativeMaxNumberOfBuckets()} */ 312 @Nullable 313 public Integer getHistogramNativeMaxNumberOfBuckets() { 314 return histogramNativeMaxNumberOfBuckets; 315 } 316 317 /** See {@code Histogram.Builder.nativeResetDuration()} */ 318 @Nullable 319 public Long getHistogramNativeResetDurationSeconds() { 320 return histogramNativeResetDurationSeconds; 321 } 322 323 /** See {@code Summary.Builder.quantile()} */ 324 @Nullable 325 public List<Double> getSummaryQuantiles() { 326 return summaryQuantiles; 327 } 328 329 /** 330 * See {@code Summary.Builder.quantile()} 331 * 332 * <p>Returns {@code null} only if {@link #getSummaryQuantiles()} is also {@code null}. Returns an 333 * empty list if {@link #getSummaryQuantiles()} are specified without specifying errors. If the 334 * list is not empty, it has the same size as {@link #getSummaryQuantiles()}. 335 */ 336 @Nullable 337 public List<Double> getSummaryQuantileErrors() { 338 if (summaryQuantiles != null) { 339 if (summaryQuantileErrors == null) { 340 return Collections.emptyList(); 341 } 342 } 343 return summaryQuantileErrors; 344 } 345 346 /** See {@code Summary.Builder.maxAgeSeconds()} */ 347 @Nullable 348 public Long getSummaryMaxAgeSeconds() { 349 return summaryMaxAgeSeconds; 350 } 351 352 /** See {@code Summary.Builder.numberOfAgeBuckets()} */ 353 @Nullable 354 public Integer getSummaryNumberOfAgeBuckets() { 355 return summaryNumberOfAgeBuckets; 356 } 357 358 /** 359 * Note that this will remove entries from {@code propertySource}. This is because we want to know 360 * if there are unused properties remaining after all properties have been loaded. 361 */ 362 static MetricsProperties load(String prefix, PropertySource propertySource) 363 throws PrometheusPropertiesException { 364 return new MetricsProperties( 365 Util.loadBoolean(prefix, EXEMPLARS_ENABLED, propertySource), 366 Util.loadBoolean(prefix, HISTOGRAM_NATIVE_ONLY, propertySource), 367 Util.loadBoolean(prefix, HISTOGRAM_CLASSIC_ONLY, propertySource), 368 Util.loadDoubleList(prefix, HISTOGRAM_CLASSIC_UPPER_BOUNDS, propertySource), 369 Util.loadInteger(prefix, HISTOGRAM_NATIVE_INITIAL_SCHEMA, propertySource), 370 Util.loadDouble(prefix, HISTOGRAM_NATIVE_MIN_ZERO_THRESHOLD, propertySource), 371 Util.loadDouble(prefix, HISTOGRAM_NATIVE_MAX_ZERO_THRESHOLD, propertySource), 372 Util.loadInteger(prefix, HISTOGRAM_NATIVE_MAX_NUMBER_OF_BUCKETS, propertySource), 373 Util.loadLong(prefix, HISTOGRAM_NATIVE_RESET_DURATION_SECONDS, propertySource), 374 Util.loadDoubleList(prefix, SUMMARY_QUANTILES, propertySource), 375 Util.loadDoubleList(prefix, SUMMARY_QUANTILE_ERRORS, propertySource), 376 Util.loadLong(prefix, SUMMARY_MAX_AGE_SECONDS, propertySource), 377 Util.loadInteger(prefix, SUMMARY_NUMBER_OF_AGE_BUCKETS, propertySource), 378 prefix); 379 } 380 381 public static Builder builder() { 382 return new Builder(); 383 } 384 385 public static class Builder { 386 @Nullable private Boolean exemplarsEnabled; 387 @Nullable private Boolean histogramNativeOnly; 388 @Nullable private Boolean histogramClassicOnly; 389 @Nullable private List<Double> histogramClassicUpperBounds; 390 @Nullable private Integer histogramNativeInitialSchema; 391 @Nullable private Double histogramNativeMinZeroThreshold; 392 @Nullable private Double histogramNativeMaxZeroThreshold; 393 @Nullable private Integer histogramNativeMaxNumberOfBuckets; 394 @Nullable private Long histogramNativeResetDurationSeconds; 395 @Nullable private List<Double> summaryQuantiles; 396 @Nullable private List<Double> summaryQuantileErrors; 397 @Nullable private Long summaryMaxAgeSeconds; 398 @Nullable private Integer summaryNumberOfAgeBuckets; 399 400 private Builder() {} 401 402 public MetricsProperties build() { 403 return new MetricsProperties( 404 exemplarsEnabled, 405 histogramNativeOnly, 406 histogramClassicOnly, 407 histogramClassicUpperBounds, 408 histogramNativeInitialSchema, 409 histogramNativeMinZeroThreshold, 410 histogramNativeMaxZeroThreshold, 411 histogramNativeMaxNumberOfBuckets, 412 histogramNativeResetDurationSeconds, 413 summaryQuantiles, 414 summaryQuantileErrors, 415 summaryMaxAgeSeconds, 416 summaryNumberOfAgeBuckets); 417 } 418 419 /** See {@link MetricsProperties#getExemplarsEnabled()} */ 420 public Builder exemplarsEnabled(@Nullable Boolean exemplarsEnabled) { 421 this.exemplarsEnabled = exemplarsEnabled; 422 return this; 423 } 424 425 /** See {@link MetricsProperties#getHistogramNativeOnly()} */ 426 public Builder histogramNativeOnly(@Nullable Boolean histogramNativeOnly) { 427 this.histogramNativeOnly = histogramNativeOnly; 428 return this; 429 } 430 431 /** See {@link MetricsProperties#getHistogramClassicOnly()} */ 432 public Builder histogramClassicOnly(@Nullable Boolean histogramClassicOnly) { 433 this.histogramClassicOnly = histogramClassicOnly; 434 return this; 435 } 436 437 /** See {@link MetricsProperties#getHistogramClassicUpperBounds()} */ 438 public Builder histogramClassicUpperBounds(double... histogramClassicUpperBounds) { 439 this.histogramClassicUpperBounds = Util.toList(histogramClassicUpperBounds); 440 return this; 441 } 442 443 /** See {@link MetricsProperties#getHistogramNativeInitialSchema()} */ 444 public Builder histogramNativeInitialSchema(@Nullable Integer histogramNativeInitialSchema) { 445 this.histogramNativeInitialSchema = histogramNativeInitialSchema; 446 return this; 447 } 448 449 /** See {@link MetricsProperties#getHistogramNativeMinZeroThreshold()} */ 450 public Builder histogramNativeMinZeroThreshold( 451 @Nullable Double histogramNativeMinZeroThreshold) { 452 this.histogramNativeMinZeroThreshold = histogramNativeMinZeroThreshold; 453 return this; 454 } 455 456 /** See {@link MetricsProperties#getHistogramNativeMaxZeroThreshold()} */ 457 public Builder histogramNativeMaxZeroThreshold( 458 @Nullable Double histogramNativeMaxZeroThreshold) { 459 this.histogramNativeMaxZeroThreshold = histogramNativeMaxZeroThreshold; 460 return this; 461 } 462 463 /** See {@link MetricsProperties#getHistogramNativeMaxNumberOfBuckets()} */ 464 public Builder histogramNativeMaxNumberOfBuckets( 465 @Nullable Integer histogramNativeMaxNumberOfBuckets) { 466 this.histogramNativeMaxNumberOfBuckets = histogramNativeMaxNumberOfBuckets; 467 return this; 468 } 469 470 /** See {@link MetricsProperties#getHistogramNativeResetDurationSeconds()} */ 471 public Builder histogramNativeResetDurationSeconds( 472 @Nullable Long histogramNativeResetDurationSeconds) { 473 this.histogramNativeResetDurationSeconds = histogramNativeResetDurationSeconds; 474 return this; 475 } 476 477 /** See {@link MetricsProperties#getSummaryQuantiles()} */ 478 public Builder summaryQuantiles(double... summaryQuantiles) { 479 this.summaryQuantiles = Util.toList(summaryQuantiles); 480 return this; 481 } 482 483 /** See {@link MetricsProperties#getSummaryQuantileErrors()} */ 484 public Builder summaryQuantileErrors(double... summaryQuantileErrors) { 485 this.summaryQuantileErrors = Util.toList(summaryQuantileErrors); 486 return this; 487 } 488 489 /** See {@link MetricsProperties#getSummaryMaxAgeSeconds()} */ 490 public Builder summaryMaxAgeSeconds(@Nullable Long summaryMaxAgeSeconds) { 491 this.summaryMaxAgeSeconds = summaryMaxAgeSeconds; 492 return this; 493 } 494 495 /** See {@link MetricsProperties#getSummaryNumberOfAgeBuckets()} */ 496 public Builder summaryNumberOfAgeBuckets(@Nullable Integer summaryNumberOfAgeBuckets) { 497 this.summaryNumberOfAgeBuckets = summaryNumberOfAgeBuckets; 498 return this; 499 } 500 } 501}