001package io.prometheus.metrics.model.snapshots; 002 003import java.util.ArrayList; 004import java.util.Arrays; 005import java.util.Collection; 006import java.util.Collections; 007import java.util.Comparator; 008import java.util.List; 009 010/** 011 * Base class for metric snapshots. 012 */ 013public abstract class MetricSnapshot { 014 015 private final MetricMetadata metadata; 016 protected final List<? extends DataPointSnapshot> dataPoints; 017 018 protected MetricSnapshot(MetricMetadata metadata, DataPointSnapshot... dataPoints) { 019 this(metadata, Arrays.asList(dataPoints)); 020 } 021 022 protected MetricSnapshot(MetricMetadata metadata, Collection<? extends DataPointSnapshot> dataPoints) { 023 if (metadata == null) { 024 throw new NullPointerException("metadata"); 025 } 026 if (dataPoints == null) { 027 throw new NullPointerException("dataPoints"); 028 } 029 this.metadata = metadata; 030 List<? extends DataPointSnapshot> dataCopy = new ArrayList<>(dataPoints); 031 dataCopy.sort(Comparator.comparing(DataPointSnapshot::getLabels)); 032 this.dataPoints = Collections.unmodifiableList(dataCopy); 033 validateLabels(); 034 } 035 036 public MetricMetadata getMetadata() { 037 return metadata; 038 } 039 040 public abstract List<? extends DataPointSnapshot> getDataPoints(); 041 042 protected void validateLabels() { 043 // Verify that labels are unique (the same set of names/values must not be used multiple times for the same metric). 044 for (int i = 0; i < dataPoints.size() - 1; i++) { 045 if (dataPoints.get(i).getLabels().equals(dataPoints.get(i + 1).getLabels())) { 046 throw new DuplicateLabelsException(metadata, dataPoints.get(i).getLabels()); 047 } 048 } 049 // Should we verify that all entries in data have the same label names? 050 // No. They should have the same label names, but according to OpenMetrics this is not a MUST. 051 } 052 053 public static abstract class Builder<T extends Builder<T>> { 054 055 private String name; 056 private String help; 057 private Unit unit; 058 059 /** 060 * The name is required. 061 * If the name is missing or invalid, {@code build()} will throw an {@link IllegalArgumentException}. 062 * See {@link PrometheusNaming#isValidMetricName(String)} for info on valid metric names. 063 */ 064 public T name(String name) { 065 this.name = name; 066 return self(); 067 } 068 069 public T help(String help) { 070 this.help = help; 071 return self(); 072 } 073 074 public T unit(Unit unit) { 075 this.unit = unit; 076 return self(); 077 } 078 079 public abstract MetricSnapshot build(); 080 081 protected MetricMetadata buildMetadata() { 082 return new MetricMetadata(name, help, unit); 083 } 084 085 protected abstract T self(); 086 } 087}