66import static io .prometheus .metrics .expositionformats .TextFormatUtil .writeLong ;
77import static io .prometheus .metrics .expositionformats .TextFormatUtil .writeName ;
88import static io .prometheus .metrics .expositionformats .TextFormatUtil .writeOpenMetricsTimestamp ;
9- import static io .prometheus .metrics .model .snapshots .SnapshotEscaper .getExpositionBaseMetadataName ;
9+ import static io .prometheus .metrics .model .snapshots .SnapshotEscaper .getOriginalMetadataName ;
1010import static io .prometheus .metrics .model .snapshots .SnapshotEscaper .getSnapshotLabelName ;
1111
1212import io .prometheus .metrics .config .EscapingScheme ;
4040
4141/**
4242 * Write the OpenMetrics 2.0 text format. Unlike the OM1 writer, this writer outputs metric names as
43- * provided by the user — no {@code _total} or unit suffix appending . The {@code _info} suffix is
44- * enforced per the OM2 spec (MUST). This is experimental and subject to change as the <a
43+ * provided by the user, without appending {@code _total} or unit suffixes . The {@code _info} suffix
44+ * is enforced per the OM2 spec (MUST). This is experimental and subject to change as the <a
4545 * href="https://github.com/prometheus/docs/blob/main/docs/specs/om/open_metrics_spec_2_0.md">OpenMetrics
4646 * 2.0 specification</a> evolves.
4747 */
@@ -89,6 +89,7 @@ public OpenMetrics2TextFormatWriter build() {
8989 public static final String CONTENT_TYPE =
9090 "application/openmetrics-text; version=2.0.0; charset=utf-8" ;
9191 private final OpenMetrics2Properties openMetrics2Properties ;
92+ private final boolean createdTimestampsEnabled ;
9293 private final boolean exemplarsOnAllMetricTypesEnabled ;
9394 private final OpenMetricsTextFormatWriter om1Writer ;
9495
@@ -102,6 +103,7 @@ public OpenMetrics2TextFormatWriter(
102103 boolean createdTimestampsEnabled ,
103104 boolean exemplarsOnAllMetricTypesEnabled ) {
104105 this .openMetrics2Properties = openMetrics2Properties ;
106+ this .createdTimestampsEnabled = createdTimestampsEnabled ;
105107 this .exemplarsOnAllMetricTypesEnabled = exemplarsOnAllMetricTypesEnabled ;
106108 this .om1Writer =
107109 new OpenMetricsTextFormatWriter (createdTimestampsEnabled , exemplarsOnAllMetricTypesEnabled );
@@ -170,8 +172,8 @@ public void write(OutputStream out, MetricSnapshots metricSnapshots, EscapingSch
170172 private void writeCounter (Writer writer , CounterSnapshot snapshot , EscapingScheme scheme )
171173 throws IOException {
172174 MetricMetadata metadata = snapshot .getMetadata ();
173- // OM2: use the name as provided by the user , no _total appending
174- String counterName = getExpositionBaseMetadataName (metadata , scheme );
175+ // OM2: use the original name , no _total or unit suffix appending.
176+ String counterName = getOriginalMetadataName (metadata , scheme );
175177 writeMetadataWithName (writer , counterName , "counter" , metadata );
176178 for (CounterSnapshot .CounterDataPointSnapshot data : snapshot .getDataPoints ()) {
177179 writeNameAndLabels (writer , counterName , null , data .getLabels (), scheme );
@@ -192,7 +194,7 @@ private void writeCounter(Writer writer, CounterSnapshot snapshot, EscapingSchem
192194 private void writeGauge (Writer writer , GaugeSnapshot snapshot , EscapingScheme scheme )
193195 throws IOException {
194196 MetricMetadata metadata = snapshot .getMetadata ();
195- String name = getExpositionBaseMetadataName (metadata , scheme );
197+ String name = getOriginalMetadataName (metadata , scheme );
196198 writeMetadataWithName (writer , name , "gauge" , metadata );
197199 for (GaugeSnapshot .GaugeDataPointSnapshot data : snapshot .getDataPoints ()) {
198200 writeNameAndLabels (writer , name , null , data .getLabels (), scheme );
@@ -209,12 +211,12 @@ private void writeHistogram(Writer writer, HistogramSnapshot snapshot, EscapingS
209211 throws IOException {
210212 boolean compositeHistogram =
211213 openMetrics2Properties .getCompositeValues () || openMetrics2Properties .getNativeHistograms ();
214+ MetricMetadata metadata = snapshot .getMetadata ();
215+ String name = getOriginalMetadataName (metadata , scheme );
212216 if (!compositeHistogram && !openMetrics2Properties .getExemplarCompliance ()) {
213- om1Writer . writeHistogram (writer , snapshot , scheme );
217+ writeClassicHistogram (writer , name , snapshot , scheme );
214218 return ;
215219 }
216- MetricMetadata metadata = snapshot .getMetadata ();
217- String name = getExpositionBaseMetadataName (metadata , scheme );
218220 if (snapshot .isGaugeHistogram ()) {
219221 writeMetadataWithName (writer , name , "gaugehistogram" , metadata );
220222 for (HistogramSnapshot .HistogramDataPointSnapshot data : snapshot .getDataPoints ()) {
@@ -236,6 +238,88 @@ private void writeHistogram(Writer writer, HistogramSnapshot snapshot, EscapingS
236238 }
237239 }
238240
241+ private void writeClassicHistogram (
242+ Writer writer , String name , HistogramSnapshot snapshot , EscapingScheme scheme )
243+ throws IOException {
244+ if (snapshot .isGaugeHistogram ()) {
245+ writeMetadataWithName (writer , name , "gaugehistogram" , snapshot .getMetadata ());
246+ writeClassicHistogramDataPoints (writer , name , "_gcount" , "_gsum" , snapshot , scheme );
247+ } else {
248+ writeMetadataWithName (writer , name , "histogram" , snapshot .getMetadata ());
249+ writeClassicHistogramDataPoints (writer , name , "_count" , "_sum" , snapshot , scheme );
250+ }
251+ }
252+
253+ private void writeClassicHistogramDataPoints (
254+ Writer writer ,
255+ String name ,
256+ String countSuffix ,
257+ String sumSuffix ,
258+ HistogramSnapshot snapshot ,
259+ EscapingScheme scheme )
260+ throws IOException {
261+ for (HistogramSnapshot .HistogramDataPointSnapshot data : snapshot .getDataPoints ()) {
262+ ClassicHistogramBuckets buckets = getClassicBuckets (data );
263+ Exemplars exemplars = data .getExemplars ();
264+ long cumulativeCount = 0 ;
265+ for (int i = 0 ; i < buckets .size (); i ++) {
266+ cumulativeCount += buckets .getCount (i );
267+ writeNameAndLabels (
268+ writer , name , "_bucket" , data .getLabels (), scheme , "le" , buckets .getUpperBound (i ));
269+ writeLong (writer , cumulativeCount );
270+ Exemplar exemplar ;
271+ if (i == 0 ) {
272+ exemplar = exemplars .get (Double .NEGATIVE_INFINITY , buckets .getUpperBound (i ));
273+ } else {
274+ exemplar = exemplars .get (buckets .getUpperBound (i - 1 ), buckets .getUpperBound (i ));
275+ }
276+ writeScrapeTimestampAndExemplar (writer , data , exemplar , scheme );
277+ }
278+ if (data .hasCount () && data .hasSum ()) {
279+ writeClassicCountAndSum (writer , name , data , countSuffix , sumSuffix , exemplars , scheme );
280+ }
281+ writeClassicCreated (writer , name , data , scheme );
282+ }
283+ }
284+
285+ private void writeClassicCountAndSum (
286+ Writer writer ,
287+ String name ,
288+ HistogramSnapshot .HistogramDataPointSnapshot data ,
289+ String countSuffix ,
290+ String sumSuffix ,
291+ Exemplars exemplars ,
292+ EscapingScheme scheme )
293+ throws IOException {
294+ writeNameAndLabels (writer , name , countSuffix , data .getLabels (), scheme );
295+ writeLong (writer , data .getCount ());
296+ if (exemplarsOnAllMetricTypesEnabled ) {
297+ writeScrapeTimestampAndExemplar (writer , data , exemplars .getLatest (), scheme );
298+ } else {
299+ writeScrapeTimestampAndExemplar (writer , data , null , scheme );
300+ }
301+ writeNameAndLabels (writer , name , sumSuffix , data .getLabels (), scheme );
302+ writeDouble (writer , data .getSum ());
303+ writeScrapeTimestampAndExemplar (writer , data , null , scheme );
304+ }
305+
306+ private void writeClassicCreated (
307+ Writer writer ,
308+ String name ,
309+ HistogramSnapshot .HistogramDataPointSnapshot data ,
310+ EscapingScheme scheme )
311+ throws IOException {
312+ if (createdTimestampsEnabled && data .hasCreatedTimestamp ()) {
313+ writeNameAndLabels (writer , name , "_created" , data .getLabels (), scheme );
314+ writeOpenMetricsTimestamp (writer , data .getCreatedTimestampMillis ());
315+ if (data .hasScrapeTimestamp ()) {
316+ writer .write (' ' );
317+ writeOpenMetricsTimestamp (writer , data .getScrapeTimestampMillis ());
318+ }
319+ writer .write ('\n' );
320+ }
321+ }
322+
239323 private void writeCompositeHistogramDataPoint (
240324 Writer writer ,
241325 String name ,
@@ -398,7 +482,7 @@ private void writeSummary(Writer writer, SummarySnapshot snapshot, EscapingSchem
398482 }
399483 boolean metadataWritten = false ;
400484 MetricMetadata metadata = snapshot .getMetadata ();
401- String name = getExpositionBaseMetadataName (metadata , scheme );
485+ String name = getOriginalMetadataName (metadata , scheme );
402486 for (SummarySnapshot .SummaryDataPointSnapshot data : snapshot .getDataPoints ()) {
403487 if (data .getQuantiles ().size () == 0 && !data .hasCount () && !data .hasSum ()) {
404488 continue ;
@@ -465,7 +549,7 @@ private void writeInfo(Writer writer, InfoSnapshot snapshot, EscapingScheme sche
465549 MetricMetadata metadata = snapshot .getMetadata ();
466550 // OM2 spec: Info MetricFamily name MUST end in _info.
467551 // In OM2, TYPE/HELP use the same name as the data lines.
468- String infoName = ensureSuffix (getExpositionBaseMetadataName (metadata , scheme ), "_info" );
552+ String infoName = ensureSuffix (getOriginalMetadataName (metadata , scheme ), "_info" );
469553 writeMetadataWithName (writer , infoName , "info" , metadata );
470554 for (InfoSnapshot .InfoDataPointSnapshot data : snapshot .getDataPoints ()) {
471555 writeNameAndLabels (writer , infoName , null , data .getLabels (), scheme );
@@ -477,7 +561,7 @@ private void writeInfo(Writer writer, InfoSnapshot snapshot, EscapingScheme sche
477561 private void writeStateSet (Writer writer , StateSetSnapshot snapshot , EscapingScheme scheme )
478562 throws IOException {
479563 MetricMetadata metadata = snapshot .getMetadata ();
480- String name = getExpositionBaseMetadataName (metadata , scheme );
564+ String name = getOriginalMetadataName (metadata , scheme );
481565 writeMetadataWithName (writer , name , "stateset" , metadata );
482566 for (StateSetSnapshot .StateSetDataPointSnapshot data : snapshot .getDataPoints ()) {
483567 for (int i = 0 ; i < data .size (); i ++) {
@@ -513,7 +597,7 @@ private void writeStateSet(Writer writer, StateSetSnapshot snapshot, EscapingSch
513597 private void writeUnknown (Writer writer , UnknownSnapshot snapshot , EscapingScheme scheme )
514598 throws IOException {
515599 MetricMetadata metadata = snapshot .getMetadata ();
516- String name = getExpositionBaseMetadataName (metadata , scheme );
600+ String name = getOriginalMetadataName (metadata , scheme );
517601 writeMetadataWithName (writer , name , "unknown" , metadata );
518602 for (UnknownSnapshot .UnknownDataPointSnapshot data : snapshot .getDataPoints ()) {
519603 writeNameAndLabels (writer , name , null , data .getLabels (), scheme );
0 commit comments