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