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