001package io.prometheus.metrics.model.snapshots;
002
003import io.prometheus.metrics.config.EscapingScheme;
004import java.util.ArrayList;
005import java.util.List;
006import javax.annotation.Nullable;
007
008public class SnapshotEscaper {
009
010  private SnapshotEscaper() {}
011
012  /** Escapes the given metric names and labels with the given escaping scheme. */
013  public static MetricSnapshot escapeMetricSnapshot(MetricSnapshot v, EscapingScheme scheme) {
014    if (scheme == EscapingScheme.ALLOW_UTF8 || scheme == EscapingScheme.UNDERSCORE_ESCAPING) {
015      // we re-use the prometheus name for underscore escaping as an optimization
016      return v;
017    }
018
019    List<DataPointSnapshot> outDataPoints = new ArrayList<>();
020
021    for (DataPointSnapshot d : v.getDataPoints()) {
022      if (snapshotNeedsEscaping(d, scheme)) {
023        outDataPoints.add(d.escape(scheme));
024      } else {
025        outDataPoints.add(d);
026      }
027    }
028
029    return v.escape(scheme, outDataPoints);
030  }
031
032  static boolean snapshotNeedsEscaping(DataPointSnapshot d, EscapingScheme scheme) {
033    Labels labels = d.getLabels();
034    if (labelsNeedsEscaping(labels, scheme)) {
035      return true;
036    }
037    if (d instanceof SummarySnapshot.SummaryDataPointSnapshot) {
038      return exemplarsNeedsEscaping(
039          ((SummarySnapshot.SummaryDataPointSnapshot) d).getExemplars(), scheme);
040    }
041    if (d instanceof HistogramSnapshot.HistogramDataPointSnapshot) {
042      return exemplarsNeedsEscaping(
043          ((HistogramSnapshot.HistogramDataPointSnapshot) d).getExemplars(), scheme);
044    }
045    if (d instanceof CounterSnapshot.CounterDataPointSnapshot) {
046      return exemplarNeedsEscaping(
047          ((CounterSnapshot.CounterDataPointSnapshot) d).getExemplar(), scheme);
048    }
049    if (d instanceof UnknownSnapshot.UnknownDataPointSnapshot) {
050      return exemplarNeedsEscaping(
051          ((UnknownSnapshot.UnknownDataPointSnapshot) d).getExemplar(), scheme);
052    }
053    if (d instanceof GaugeSnapshot.GaugeDataPointSnapshot) {
054      return exemplarNeedsEscaping(
055          ((GaugeSnapshot.GaugeDataPointSnapshot) d).getExemplar(), scheme);
056    }
057
058    return false;
059  }
060
061  private static boolean labelsNeedsEscaping(Labels labels, EscapingScheme scheme) {
062    for (Label l : labels) {
063      if (PrometheusNaming.needsEscaping(l.getName(), scheme)) {
064        return true;
065      }
066    }
067    return false;
068  }
069
070  private static boolean exemplarNeedsEscaping(@Nullable Exemplar exemplar, EscapingScheme scheme) {
071    return exemplar != null && labelsNeedsEscaping(exemplar.getLabels(), scheme);
072  }
073
074  private static boolean exemplarsNeedsEscaping(Exemplars exemplars, EscapingScheme scheme) {
075    for (Exemplar exemplar : exemplars) {
076      if (labelsNeedsEscaping(exemplar.getLabels(), scheme)) {
077        return true;
078      }
079    }
080    return false;
081  }
082
083  public static String getSnapshotLabelName(Labels labels, int index, EscapingScheme scheme) {
084    if (scheme == EscapingScheme.UNDERSCORE_ESCAPING) {
085      return labels.getPrometheusName(index);
086    } else {
087      return labels.getName(index);
088    }
089  }
090
091  public static String getMetadataName(MetricMetadata metadata, EscapingScheme scheme) {
092    if (scheme == EscapingScheme.UNDERSCORE_ESCAPING) {
093      return metadata.getPrometheusName();
094    } else {
095      return metadata.getName();
096    }
097  }
098
099  public static String getExpositionBaseMetadataName(
100      MetricMetadata metadata, EscapingScheme scheme) {
101    if (scheme == EscapingScheme.UNDERSCORE_ESCAPING) {
102      return metadata.getExpositionBasePrometheusName();
103    } else {
104      return metadata.getExpositionBaseName();
105    }
106  }
107
108  public static String getOriginalMetadataName(MetricMetadata metadata, EscapingScheme scheme) {
109    if (scheme == EscapingScheme.UNDERSCORE_ESCAPING) {
110      return PrometheusNaming.prometheusName(metadata.getOriginalName());
111    } else {
112      return metadata.getOriginalName();
113    }
114  }
115
116  public static String getLegacyGaugeName(
117      MetricMetadata metadata, String rawOriginalName, EscapingScheme scheme) {
118    String legacyGaugeBaseName = stripLegacyGaugeSuffix(rawOriginalName);
119    if (legacyGaugeBaseName != null) {
120      return PrometheusNaming.escapeName(legacyGaugeBaseName, scheme);
121    }
122    return getMetadataName(metadata, scheme);
123  }
124
125  @Nullable
126  private static String stripLegacyGaugeSuffix(String rawOriginalName) {
127    if (rawOriginalName.endsWith(".created")) {
128      return rawOriginalName.substring(0, rawOriginalName.length() - ".created".length());
129    }
130    if (rawOriginalName.endsWith(".total")) {
131      return rawOriginalName.substring(0, rawOriginalName.length() - ".total".length());
132    }
133    return null;
134  }
135
136  public static Labels escapeLabels(Labels labels, EscapingScheme scheme) {
137    Labels.Builder outLabelsBuilder = Labels.builder();
138
139    for (Label l : labels) {
140      outLabelsBuilder.label(PrometheusNaming.escapeName(l.getName(), scheme), l.getValue());
141    }
142
143    return outLabelsBuilder.build();
144  }
145
146  public static Exemplars escapeExemplars(Exemplars exemplars, EscapingScheme scheme) {
147    List<Exemplar> escapedExemplars = new ArrayList<>(exemplars.size());
148    for (Exemplar exemplar : exemplars) {
149      escapedExemplars.add(escapeExemplar(exemplar, scheme));
150    }
151    return Exemplars.of(escapedExemplars);
152  }
153
154  @Nullable
155  public static Exemplar escapeExemplar(@Nullable Exemplar exemplar, EscapingScheme scheme) {
156    if (exemplar == null) {
157      return null;
158    }
159    return Exemplar.builder()
160        .labels(escapeLabels(exemplar.getLabels(), scheme))
161        .timestampMillis(exemplar.getTimestampMillis())
162        .value(exemplar.getValue())
163        .build();
164  }
165}