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