001package io.prometheus.metrics.model.snapshots; 002 003/** Immutable representation of an Exemplar. */ 004public class Exemplar { 005 006 /** Label name for trace id. */ 007 public static final String TRACE_ID = "trace_id"; 008 009 /** Label name for span id. */ 010 public static final String SPAN_ID = "span_id"; 011 012 private final double value; 013 private final Labels labels; 014 private final long timestampMillis; 015 016 /** 017 * To create a new {@link Exemplar}, you can either call the constructor directly or use the 018 * Builder with {@link Exemplar#builder()}. 019 * 020 * @param value the observed value. This is required. 021 * @param labels in most cases the labels will contain the {@link #TRACE_ID} and {@link #SPAN_ID}. 022 * Must not be {@code null}. Use {@link Labels#EMPTY} if no labels are present. 023 * @param timestampMillis timestamp when the value was observed. Optional. Use 0L if not 024 * available. 025 */ 026 public Exemplar(double value, Labels labels, long timestampMillis) { 027 if (labels == null) { 028 throw new NullPointerException("Labels cannot be null. Use Labels.EMPTY."); 029 } 030 this.value = value; 031 this.labels = labels; 032 this.timestampMillis = timestampMillis; 033 } 034 035 public double getValue() { 036 return value; 037 } 038 039 /** 040 * In most cases labels will contain {@link #TRACE_ID} and {@link #SPAN_ID}, but this is not 041 * required. May be {@link Labels#EMPTY}, but may not be {@code null}. 042 */ 043 public Labels getLabels() { 044 return labels; 045 } 046 047 public boolean hasTimestamp() { 048 return timestampMillis != 0L; 049 } 050 051 /** Will return garbage if {@link #hasTimestamp()} is {@code false}. */ 052 public long getTimestampMillis() { 053 return timestampMillis; 054 } 055 056 public static Builder builder() { 057 return new Builder(); 058 } 059 060 public static class Builder { 061 062 private Double value = null; 063 private Labels labels = Labels.EMPTY; 064 private String traceId = null; 065 private String spanId = null; 066 private long timestampMillis = 0L; 067 068 private Builder() {} 069 070 public Builder value(double value) { 071 this.value = value; 072 return this; 073 } 074 075 public Builder traceId(String traceId) { 076 this.traceId = traceId; 077 return this; 078 } 079 080 public Builder spanId(String spanId) { 081 this.spanId = spanId; 082 return this; 083 } 084 085 public Builder labels(Labels labels) { 086 if (labels == null) { 087 throw new NullPointerException(); 088 } 089 this.labels = labels; 090 return this; 091 } 092 093 public Builder timestampMillis(long timestampMillis) { 094 this.timestampMillis = timestampMillis; 095 return this; 096 } 097 098 /** 099 * @throws IllegalStateException if {@link #value(double)} wasn't called. 100 */ 101 public Exemplar build() { 102 if (value == null) { 103 throw new IllegalStateException("cannot build an Exemplar without a value"); 104 } 105 Labels allLabels; 106 if (traceId != null && spanId != null) { 107 allLabels = Labels.of(TRACE_ID, traceId, SPAN_ID, spanId); 108 } else if (traceId != null) { 109 allLabels = Labels.of(TRACE_ID, traceId); 110 } else if (spanId != null) { 111 allLabels = Labels.of(SPAN_ID, spanId); 112 } else { 113 allLabels = Labels.EMPTY; 114 } 115 if (!labels.isEmpty()) { 116 allLabels = allLabels.merge(labels); 117 } 118 return new Exemplar(value, allLabels, timestampMillis); 119 } 120 } 121}