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 .getMetadataName ;
9+ import static io .prometheus .metrics .model .snapshots .SnapshotEscaper .getExpositionBaseMetadataName ;
1010import static io .prometheus .metrics .model .snapshots .SnapshotEscaper .getSnapshotLabelName ;
1111
1212import io .prometheus .metrics .config .EscapingScheme ;
4040import javax .annotation .Nullable ;
4141
4242/**
43- * Write the OpenMetrics 2.0 text format. This is currently a skeleton implementation that produces
44- * identical output to OpenMetrics 1.0, with infrastructure for future OM2 features. This is
45- * experimental and subject to change as the <a
43+ * Write the OpenMetrics 2.0 text format. Unlike the OM1 writer, this writer outputs metric names as
44+ * provided by the user — no {@code _total} or unit suffix appending. The {@code _info} suffix is
45+ * enforced per the OM2 spec (MUST). This is experimental and subject to change as the <a
4646 * href="https://github.com/prometheus/docs/blob/main/docs/specs/om/open_metrics_spec_2_0.md">OpenMetrics
4747 * 2.0 specification</a> evolves.
4848 */
@@ -171,22 +171,24 @@ public void write(OutputStream out, MetricSnapshots metricSnapshots, EscapingSch
171171 private void writeCounter (Writer writer , CounterSnapshot snapshot , EscapingScheme scheme )
172172 throws IOException {
173173 MetricMetadata metadata = snapshot .getMetadata ();
174- writeMetadata (writer , "counter" , metadata , scheme );
174+ // OM2: use the name as provided by the user, no _total appending
175+ String counterName = getExpositionBaseMetadataName (metadata , scheme );
176+ writeMetadataWithName (writer , counterName , "counter" , metadata );
175177 for (CounterSnapshot .CounterDataPointSnapshot data : snapshot .getDataPoints ()) {
176- writeNameAndLabels (
177- writer , getMetadataName (metadata , scheme ), "_total" , data .getLabels (), scheme );
178+ writeNameAndLabels (writer , counterName , null , data .getLabels (), scheme );
178179 writeDouble (writer , data .getValue ());
179180 writeScrapeTimestampAndExemplar (writer , data , data .getExemplar (), scheme );
180- writeCreated (writer , metadata , data , scheme );
181+ writeCreated (writer , counterName , data , scheme );
181182 }
182183 }
183184
184185 private void writeGauge (Writer writer , GaugeSnapshot snapshot , EscapingScheme scheme )
185186 throws IOException {
186187 MetricMetadata metadata = snapshot .getMetadata ();
187- writeMetadata (writer , "gauge" , metadata , scheme );
188+ String name = getExpositionBaseMetadataName (metadata , scheme );
189+ writeMetadataWithName (writer , name , "gauge" , metadata );
188190 for (GaugeSnapshot .GaugeDataPointSnapshot data : snapshot .getDataPoints ()) {
189- writeNameAndLabels (writer , getMetadataName ( metadata , scheme ) , null , data .getLabels (), scheme );
191+ writeNameAndLabels (writer , name , null , data .getLabels (), scheme );
190192 writeDouble (writer , data .getValue ());
191193 if (exemplarsOnAllMetricTypesEnabled ) {
192194 writeScrapeTimestampAndExemplar (writer , data , data .getExemplar (), scheme );
@@ -199,20 +201,21 @@ private void writeGauge(Writer writer, GaugeSnapshot snapshot, EscapingScheme sc
199201 private void writeHistogram (Writer writer , HistogramSnapshot snapshot , EscapingScheme scheme )
200202 throws IOException {
201203 MetricMetadata metadata = snapshot .getMetadata ();
204+ String name = getExpositionBaseMetadataName (metadata , scheme );
202205 if (snapshot .isGaugeHistogram ()) {
203- writeMetadata (writer , "gaugehistogram" , metadata , scheme );
206+ writeMetadataWithName (writer , name , "gaugehistogram" , metadata );
204207 writeClassicHistogramBuckets (
205- writer , metadata , "_gcount" , "_gsum" , snapshot .getDataPoints (), scheme );
208+ writer , name , "_gcount" , "_gsum" , snapshot .getDataPoints (), scheme );
206209 } else {
207- writeMetadata (writer , "histogram" , metadata , scheme );
210+ writeMetadataWithName (writer , name , "histogram" , metadata );
208211 writeClassicHistogramBuckets (
209- writer , metadata , "_count" , "_sum" , snapshot .getDataPoints (), scheme );
212+ writer , name , "_count" , "_sum" , snapshot .getDataPoints (), scheme );
210213 }
211214 }
212215
213216 private void writeClassicHistogramBuckets (
214217 Writer writer ,
215- MetricMetadata metadata ,
218+ String name ,
216219 String countSuffix ,
217220 String sumSuffix ,
218221 List <HistogramSnapshot .HistogramDataPointSnapshot > dataList ,
@@ -225,13 +228,7 @@ private void writeClassicHistogramBuckets(
225228 for (int i = 0 ; i < buckets .size (); i ++) {
226229 cumulativeCount += buckets .getCount (i );
227230 writeNameAndLabels (
228- writer ,
229- getMetadataName (metadata , scheme ),
230- "_bucket" ,
231- data .getLabels (),
232- scheme ,
233- "le" ,
234- buckets .getUpperBound (i ));
231+ writer , name , "_bucket" , data .getLabels (), scheme , "le" , buckets .getUpperBound (i ));
235232 writeLong (writer , cumulativeCount );
236233 Exemplar exemplar ;
237234 if (i == 0 ) {
@@ -243,9 +240,9 @@ private void writeClassicHistogramBuckets(
243240 }
244241 // In OpenMetrics format, histogram _count and _sum are either both present or both absent.
245242 if (data .hasCount () && data .hasSum ()) {
246- writeCountAndSum (writer , metadata , data , countSuffix , sumSuffix , exemplars , scheme );
243+ writeCountAndSum (writer , name , data , countSuffix , sumSuffix , exemplars , scheme );
247244 }
248- writeCreated (writer , metadata , data , scheme );
245+ writeCreated (writer , name , data , scheme );
249246 }
250247 }
251248
@@ -263,12 +260,13 @@ private void writeSummary(Writer writer, SummarySnapshot snapshot, EscapingSchem
263260 throws IOException {
264261 boolean metadataWritten = false ;
265262 MetricMetadata metadata = snapshot .getMetadata ();
263+ String name = getExpositionBaseMetadataName (metadata , scheme );
266264 for (SummarySnapshot .SummaryDataPointSnapshot data : snapshot .getDataPoints ()) {
267265 if (data .getQuantiles ().size () == 0 && !data .hasCount () && !data .hasSum ()) {
268266 continue ;
269267 }
270268 if (!metadataWritten ) {
271- writeMetadata (writer , "summary" , metadata , scheme );
269+ writeMetadataWithName (writer , name , "summary" , metadata );
272270 metadataWritten = true ;
273271 }
274272 Exemplars exemplars = data .getExemplars ();
@@ -280,13 +278,7 @@ private void writeSummary(Writer writer, SummarySnapshot snapshot, EscapingSchem
280278 int exemplarIndex = 1 ;
281279 for (Quantile quantile : data .getQuantiles ()) {
282280 writeNameAndLabels (
283- writer ,
284- getMetadataName (metadata , scheme ),
285- null ,
286- data .getLabels (),
287- scheme ,
288- "quantile" ,
289- quantile .getQuantile ());
281+ writer , name , null , data .getLabels (), scheme , "quantile" , quantile .getQuantile ());
290282 writeDouble (writer , quantile .getValue ());
291283 if (exemplars .size () > 0 && exemplarsOnAllMetricTypesEnabled ) {
292284 exemplarIndex = (exemplarIndex + 1 ) % exemplars .size ();
@@ -296,18 +288,20 @@ private void writeSummary(Writer writer, SummarySnapshot snapshot, EscapingSchem
296288 }
297289 }
298290 // Unlike histograms, summaries can have only a count or only a sum according to OpenMetrics.
299- writeCountAndSum (writer , metadata , data , "_count" , "_sum" , exemplars , scheme );
300- writeCreated (writer , metadata , data , scheme );
291+ writeCountAndSum (writer , name , data , "_count" , "_sum" , exemplars , scheme );
292+ writeCreated (writer , name , data , scheme );
301293 }
302294 }
303295
304296 private void writeInfo (Writer writer , InfoSnapshot snapshot , EscapingScheme scheme )
305297 throws IOException {
306298 MetricMetadata metadata = snapshot .getMetadata ();
307- writeMetadata (writer , "info" , metadata , scheme );
299+ // OM2 spec: Info MetricFamily name MUST end in _info.
300+ // In OM2, TYPE/HELP use the same name as the data lines.
301+ String infoName = ensureSuffix (getExpositionBaseMetadataName (metadata , scheme ), "_info" );
302+ writeMetadataWithName (writer , infoName , "info" , metadata );
308303 for (InfoSnapshot .InfoDataPointSnapshot data : snapshot .getDataPoints ()) {
309- writeNameAndLabels (
310- writer , getMetadataName (metadata , scheme ), "_info" , data .getLabels (), scheme );
304+ writeNameAndLabels (writer , infoName , null , data .getLabels (), scheme );
311305 writer .write ("1" );
312306 writeScrapeTimestampAndExemplar (writer , data , null , scheme );
313307 }
@@ -316,10 +310,11 @@ private void writeInfo(Writer writer, InfoSnapshot snapshot, EscapingScheme sche
316310 private void writeStateSet (Writer writer , StateSetSnapshot snapshot , EscapingScheme scheme )
317311 throws IOException {
318312 MetricMetadata metadata = snapshot .getMetadata ();
319- writeMetadata (writer , "stateset" , metadata , scheme );
313+ String name = getExpositionBaseMetadataName (metadata , scheme );
314+ writeMetadataWithName (writer , name , "stateset" , metadata );
320315 for (StateSetSnapshot .StateSetDataPointSnapshot data : snapshot .getDataPoints ()) {
321316 for (int i = 0 ; i < data .size (); i ++) {
322- writer .write (getMetadataName ( metadata , scheme ) );
317+ writer .write (name );
323318 writer .write ('{' );
324319 Labels labels = data .getLabels ();
325320 for (int j = 0 ; j < labels .size (); j ++) {
@@ -334,7 +329,7 @@ private void writeStateSet(Writer writer, StateSetSnapshot snapshot, EscapingSch
334329 if (!labels .isEmpty ()) {
335330 writer .write ("," );
336331 }
337- writer .write (getMetadataName ( metadata , scheme ) );
332+ writer .write (name );
338333 writer .write ("=\" " );
339334 writeEscapedString (writer , data .getName (i ));
340335 writer .write ("\" } " );
@@ -351,9 +346,10 @@ private void writeStateSet(Writer writer, StateSetSnapshot snapshot, EscapingSch
351346 private void writeUnknown (Writer writer , UnknownSnapshot snapshot , EscapingScheme scheme )
352347 throws IOException {
353348 MetricMetadata metadata = snapshot .getMetadata ();
354- writeMetadata (writer , "unknown" , metadata , scheme );
349+ String name = getExpositionBaseMetadataName (metadata , scheme );
350+ writeMetadataWithName (writer , name , "unknown" , metadata );
355351 for (UnknownSnapshot .UnknownDataPointSnapshot data : snapshot .getDataPoints ()) {
356- writeNameAndLabels (writer , getMetadataName ( metadata , scheme ) , null , data .getLabels (), scheme );
352+ writeNameAndLabels (writer , name , null , data .getLabels (), scheme );
357353 writeDouble (writer , data .getValue ());
358354 if (exemplarsOnAllMetricTypesEnabled ) {
359355 writeScrapeTimestampAndExemplar (writer , data , data .getExemplar (), scheme );
@@ -365,16 +361,15 @@ private void writeUnknown(Writer writer, UnknownSnapshot snapshot, EscapingSchem
365361
366362 private void writeCountAndSum (
367363 Writer writer ,
368- MetricMetadata metadata ,
364+ String name ,
369365 DistributionDataPointSnapshot data ,
370366 String countSuffix ,
371367 String sumSuffix ,
372368 Exemplars exemplars ,
373369 EscapingScheme scheme )
374370 throws IOException {
375371 if (data .hasCount ()) {
376- writeNameAndLabels (
377- writer , getMetadataName (metadata , scheme ), countSuffix , data .getLabels (), scheme );
372+ writeNameAndLabels (writer , name , countSuffix , data .getLabels (), scheme );
378373 writeLong (writer , data .getCount ());
379374 if (exemplarsOnAllMetricTypesEnabled ) {
380375 writeScrapeTimestampAndExemplar (writer , data , exemplars .getLatest (), scheme );
@@ -383,19 +378,17 @@ private void writeCountAndSum(
383378 }
384379 }
385380 if (data .hasSum ()) {
386- writeNameAndLabels (
387- writer , getMetadataName (metadata , scheme ), sumSuffix , data .getLabels (), scheme );
381+ writeNameAndLabels (writer , name , sumSuffix , data .getLabels (), scheme );
388382 writeDouble (writer , data .getSum ());
389383 writeScrapeTimestampAndExemplar (writer , data , null , scheme );
390384 }
391385 }
392386
393387 private void writeCreated (
394- Writer writer , MetricMetadata metadata , DataPointSnapshot data , EscapingScheme scheme )
388+ Writer writer , String name , DataPointSnapshot data , EscapingScheme scheme )
395389 throws IOException {
396390 if (createdTimestampsEnabled && data .hasCreatedTimestamp ()) {
397- writeNameAndLabels (
398- writer , getMetadataName (metadata , scheme ), "_created" , data .getLabels (), scheme );
391+ writeNameAndLabels (writer , name , "_created" , data .getLabels (), scheme );
399392 writeOpenMetricsTimestamp (writer , data .getCreatedTimestampMillis ());
400393 if (data .hasScrapeTimestamp ()) {
401394 writer .write (' ' );
@@ -466,27 +459,33 @@ private void writeScrapeTimestampAndExemplar(
466459 writer .write ('\n' );
467460 }
468461
469- private void writeMetadata (
470- Writer writer , String typeName , MetricMetadata metadata , EscapingScheme scheme )
471- throws IOException {
462+ private void writeMetadataWithName (
463+ Writer writer , String name , String typeName , MetricMetadata metadata ) throws IOException {
472464 writer .write ("# TYPE " );
473- writeName (writer , getMetadataName ( metadata , scheme ) , NameType .Metric );
465+ writeName (writer , name , NameType .Metric );
474466 writer .write (' ' );
475467 writer .write (typeName );
476468 writer .write ('\n' );
477469 if (metadata .getUnit () != null ) {
478470 writer .write ("# UNIT " );
479- writeName (writer , getMetadataName ( metadata , scheme ) , NameType .Metric );
471+ writeName (writer , name , NameType .Metric );
480472 writer .write (' ' );
481473 writeEscapedString (writer , metadata .getUnit ().toString ());
482474 writer .write ('\n' );
483475 }
484476 if (metadata .getHelp () != null && !metadata .getHelp ().isEmpty ()) {
485477 writer .write ("# HELP " );
486- writeName (writer , getMetadataName ( metadata , scheme ) , NameType .Metric );
478+ writeName (writer , name , NameType .Metric );
487479 writer .write (' ' );
488480 writeEscapedString (writer , metadata .getHelp ());
489481 writer .write ('\n' );
490482 }
491483 }
484+
485+ private static String ensureSuffix (String name , String suffix ) {
486+ if (name .endsWith (suffix )) {
487+ return name ;
488+ }
489+ return name + suffix ;
490+ }
492491}
0 commit comments