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