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