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