001package io.prometheus.metrics.model.snapshots;
002
003import io.prometheus.metrics.annotations.StableApi;
004import io.prometheus.metrics.config.EscapingScheme;
005
006@SuppressWarnings("this-escape")
007@StableApi
008public abstract class DataPointSnapshot {
009  private final Labels labels;
010  private final long createdTimestampMillis;
011  private final long scrapeTimestampMillis;
012
013  protected DataPointSnapshot(
014      Labels labels, long createdTimestampMillis, long scrapeTimestampMillis, boolean internal) {
015    this.labels = labels;
016    this.createdTimestampMillis = createdTimestampMillis;
017    this.scrapeTimestampMillis = scrapeTimestampMillis;
018    if (!internal) {
019      validate();
020    }
021  }
022
023  private void validate() {
024    if (labels == null) {
025      throw new IllegalArgumentException(
026          "Labels cannot be null. Use Labels.EMPTY if there are no labels.");
027    }
028    if (createdTimestampMillis < 0) {
029      throw new IllegalArgumentException(
030          "Created timestamp cannot be negative. "
031              + "Use 0 if the metric doesn't have a created timestamp.");
032    }
033    if (scrapeTimestampMillis < 0) {
034      throw new IllegalArgumentException(
035          "Scrape timestamp cannot be negative. "
036              + "Use 0 to indicate that the Prometheus server should set the scrape timestamp.");
037    }
038    if (hasCreatedTimestamp() && hasScrapeTimestamp()) {
039      if (scrapeTimestampMillis < createdTimestampMillis) {
040        throw new IllegalArgumentException(
041            "The scrape timestamp cannot be before the created timestamp");
042      }
043    }
044  }
045
046  public Labels getLabels() {
047    return labels;
048  }
049
050  public boolean hasScrapeTimestamp() {
051    return scrapeTimestampMillis != 0L;
052  }
053
054  /** This will only return a reasonable value if {@link #hasScrapeTimestamp()} is true. */
055  public long getScrapeTimestampMillis() {
056    return scrapeTimestampMillis;
057  }
058
059  public boolean hasCreatedTimestamp() {
060    return createdTimestampMillis != 0L;
061  }
062
063  /**
064   * This will only return a reasonable value if {@link #hasCreatedTimestamp()} is true. Some
065   * metrics like Gauge don't have created timestamps. For these metrics {@link
066   * #hasCreatedTimestamp()} is always false.
067   */
068  public long getCreatedTimestampMillis() {
069    return createdTimestampMillis;
070  }
071
072  public abstract static class Builder<T extends Builder<T>> {
073
074    protected Labels labels = Labels.EMPTY;
075    protected long scrapeTimestampMillis = 0L;
076
077    public T labels(Labels labels) {
078      this.labels = labels;
079      return self();
080    }
081
082    /**
083     * In most cases you should not set a scrape timestamp, because the scrape timestamp is set by
084     * the Prometheus server during scraping. Exceptions include mirroring metrics with given
085     * timestamps from other metric sources.
086     */
087    public T scrapeTimestampMillis(long scrapeTimestampMillis) {
088      this.scrapeTimestampMillis = scrapeTimestampMillis;
089      return self();
090    }
091
092    protected abstract T self();
093  }
094
095  abstract DataPointSnapshot escape(EscapingScheme escapingScheme);
096}