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 if (exemplars.isEmpty()) { 028 this.exemplars = Collections.emptyList(); 029 return; 030 } 031 List<Exemplar> copy = new ArrayList<>(exemplars.size()); 032 for (Exemplar exemplar : exemplars) { 033 if (exemplar == null) { 034 throw new NullPointerException("Illegal null value in Exemplars"); 035 } 036 copy.add(exemplar); 037 } 038 this.exemplars = Collections.unmodifiableList(copy); 039 } 040 041 /** 042 * Create a new Exemplars instance. You can either create Exemplars with one of the static {@code 043 * Exemplars.of(...)} methods, or you can use the {@link Exemplars#builder()}. 044 * 045 * @param exemplars a copy of the exemplars collection will be created. 046 */ 047 public static Exemplars of(Collection<Exemplar> exemplars) { 048 return exemplars.isEmpty() ? EMPTY : new Exemplars(exemplars); 049 } 050 051 /** 052 * Create a new Exemplars instance. You can either create Exemplars with one of the static {@code 053 * Exemplars.of(...)} methods, or you can use the {@link Exemplars#builder()}. 054 * 055 * @param exemplars a copy of the exemplars array will be created. 056 */ 057 public static Exemplars of(Exemplar... exemplars) { 058 return exemplars.length == 0 ? EMPTY : new Exemplars(Arrays.asList(exemplars)); 059 } 060 061 @Override 062 public Iterator<Exemplar> iterator() { 063 return exemplars.iterator(); 064 } 065 066 public int size() { 067 return exemplars.size(); 068 } 069 070 @Nullable 071 public Exemplar get(int index) { 072 return exemplars.get(index); 073 } 074 075 /** 076 * This is used by classic histograms to find an exemplar with a value between lowerBound and 077 * upperBound. If there is more than one exemplar within the bounds the one with the newest time 078 * stamp is returned. 079 */ 080 @Nullable 081 public Exemplar get(double lowerBound, double upperBound) { 082 Exemplar result = null; 083 for (Exemplar exemplar : exemplars) { 084 double value = exemplar.getValue(); 085 if (value > lowerBound && value <= upperBound) { 086 if (result == null) { 087 result = exemplar; 088 } else if (result.hasTimestamp() && exemplar.hasTimestamp()) { 089 if (exemplar.getTimestampMillis() > result.getTimestampMillis()) { 090 result = exemplar; 091 } 092 } 093 } 094 } 095 return result; 096 } 097 098 /** Find the Exemplar with the newest timestamp. May return {@code null}. */ 099 @Nullable 100 public Exemplar getLatest() { 101 Exemplar latest = null; 102 for (Exemplar candidate : exemplars) { 103 if (candidate == null) { 104 continue; 105 } 106 if (latest == null) { 107 latest = candidate; 108 continue; 109 } 110 if (!latest.hasTimestamp()) { 111 latest = candidate; 112 continue; 113 } 114 if (candidate.hasTimestamp()) { 115 if (latest.getTimestampMillis() < candidate.getTimestampMillis()) { 116 latest = candidate; 117 } 118 } 119 } 120 return latest; 121 } 122 123 public static Builder builder() { 124 return new Builder(); 125 } 126 127 public static class Builder { 128 129 private final ArrayList<Exemplar> exemplars = new ArrayList<>(); 130 131 private Builder() {} 132 133 /** Add an exemplar. This can be called multiple times to add multiple exemplars. */ 134 public Builder exemplar(Exemplar exemplar) { 135 exemplars.add(exemplar); 136 return this; 137 } 138 139 /** Add all exemplars form the collection. */ 140 public Builder exemplars(Collection<Exemplar> exemplars) { 141 this.exemplars.addAll(exemplars); 142 return this; 143 } 144 145 public Exemplars build() { 146 return Exemplars.of(exemplars); 147 } 148 } 149}