001package io.prometheus.metrics.model.snapshots; 002 003import io.prometheus.metrics.annotations.StableApi; 004import java.util.ArrayList; 005import java.util.Arrays; 006import java.util.Collection; 007import java.util.Collections; 008import java.util.Iterator; 009import java.util.List; 010import javax.annotation.Nullable; 011 012/** 013 * Immutable container for Exemplars. 014 * 015 * <p>This is currently backed by a {@code List<Exemplar>}. May be refactored later to use a more 016 * efficient data structure. 017 */ 018@StableApi 019public class Exemplars implements Iterable<Exemplar> { 020 021 /** EMPTY means no Exemplars. */ 022 public static final Exemplars EMPTY = new Exemplars(Collections.emptyList()); 023 024 private final List<Exemplar> exemplars; 025 026 private Exemplars(Collection<Exemplar> exemplars) { 027 List<Exemplar> copy = new ArrayList<>(exemplars.size()); 028 for (Exemplar exemplar : exemplars) { 029 if (exemplar == null) { 030 throw new NullPointerException("Illegal null value in Exemplars"); 031 } 032 copy.add(exemplar); 033 } 034 this.exemplars = Collections.unmodifiableList(copy); 035 } 036 037 /** 038 * Create a new Exemplars instance. You can either create Exemplars with one of the static {@code 039 * Exemplars.of(...)} methods, or you can use the {@link Exemplars#builder()}. 040 * 041 * @param exemplars a copy of the exemplars collection will be created. 042 */ 043 public static Exemplars of(Collection<Exemplar> exemplars) { 044 return new Exemplars(exemplars); 045 } 046 047 /** 048 * Create a new Exemplars instance. You can either create Exemplars with one of the static {@code 049 * Exemplars.of(...)} methods, or you can use the {@link Exemplars#builder()}. 050 * 051 * @param exemplars a copy of the exemplars array will be created. 052 */ 053 public static Exemplars of(Exemplar... exemplars) { 054 return new Exemplars(Arrays.asList(exemplars)); 055 } 056 057 @Override 058 public Iterator<Exemplar> iterator() { 059 return exemplars.iterator(); 060 } 061 062 public int size() { 063 return exemplars.size(); 064 } 065 066 @Nullable 067 public Exemplar get(int index) { 068 return exemplars.get(index); 069 } 070 071 /** 072 * This is used by classic histograms to find an exemplar with a value between lowerBound and 073 * upperBound. If there is more than one exemplar within the bounds the one with the newest time 074 * stamp is returned. 075 */ 076 @Nullable 077 public Exemplar get(double lowerBound, double upperBound) { 078 Exemplar result = null; 079 for (Exemplar exemplar : exemplars) { 080 double value = exemplar.getValue(); 081 if (value > lowerBound && value <= upperBound) { 082 if (result == null) { 083 result = exemplar; 084 } else if (result.hasTimestamp() && exemplar.hasTimestamp()) { 085 if (exemplar.getTimestampMillis() > result.getTimestampMillis()) { 086 result = exemplar; 087 } 088 } 089 } 090 } 091 return result; 092 } 093 094 /** Find the Exemplar with the newest timestamp. May return {@code null}. */ 095 @Nullable 096 public Exemplar getLatest() { 097 Exemplar latest = null; 098 for (Exemplar candidate : exemplars) { 099 if (candidate == null) { 100 continue; 101 } 102 if (latest == null) { 103 latest = candidate; 104 continue; 105 } 106 if (!latest.hasTimestamp()) { 107 latest = candidate; 108 continue; 109 } 110 if (candidate.hasTimestamp()) { 111 if (latest.getTimestampMillis() < candidate.getTimestampMillis()) { 112 latest = candidate; 113 } 114 } 115 } 116 return latest; 117 } 118 119 public static Builder builder() { 120 return new Builder(); 121 } 122 123 public static class Builder { 124 125 private final ArrayList<Exemplar> exemplars = new ArrayList<>(); 126 127 private Builder() {} 128 129 /** Add an exemplar. This can be called multiple times to add multiple exemplars. */ 130 public Builder exemplar(Exemplar exemplar) { 131 exemplars.add(exemplar); 132 return this; 133 } 134 135 /** Add all exemplars form the collection. */ 136 public Builder exemplars(Collection<Exemplar> exemplars) { 137 this.exemplars.addAll(exemplars); 138 return this; 139 } 140 141 public Exemplars build() { 142 return Exemplars.of(exemplars); 143 } 144 } 145}