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