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