Skip to content

Commit ba12b5f

Browse files
committed
fix timestamp style
Signed-off-by: Gregor Zeitlinger <[email protected]>
1 parent e8ae943 commit ba12b5f

File tree

10 files changed

+192
-59
lines changed

10 files changed

+192
-59
lines changed

benchmarks/src/main/java/io/prometheus/metrics/benchmarks/TextFormatUtilBenchmark.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,9 @@ public class TextFormatUtilBenchmark {
5050
}
5151

5252
private static final ExpositionFormatWriter OPEN_METRICS_TEXT_FORMAT_WRITER =
53-
new OpenMetricsTextFormatWriter(false, false);
53+
OpenMetricsTextFormatWriter.create();
5454
private static final ExpositionFormatWriter PROMETHEUS_TEXT_FORMAT_WRITER =
55-
new PrometheusTextFormatWriter(false);
55+
PrometheusTextFormatWriter.create();
5656

5757
@State(Scope.Benchmark)
5858
public static class WriterState {

prometheus-metrics-config/src/main/java/io/prometheus/metrics/config/ExporterProperties.java

+24-3
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,19 @@
66
public class ExporterProperties {
77

88
private static final String INCLUDE_CREATED_TIMESTAMPS = "includeCreatedTimestamps";
9+
// milliseconds is the default - we only provide a boolean flag to avoid a breaking change
10+
private static final String TIMESTAMPS_IN_MS = "timestampsInMs";
911
private static final String EXEMPLARS_ON_ALL_METRIC_TYPES = "exemplarsOnAllMetricTypes";
1012
private static final String PREFIX = "io.prometheus.exporter";
1113

1214
private final Boolean includeCreatedTimestamps;
15+
private final Boolean timestampsInMs;
1316
private final Boolean exemplarsOnAllMetricTypes;
1417

15-
private ExporterProperties(Boolean includeCreatedTimestamps, Boolean exemplarsOnAllMetricTypes) {
18+
private ExporterProperties(
19+
Boolean includeCreatedTimestamps, Boolean timestampsInMs, Boolean exemplarsOnAllMetricTypes) {
1620
this.includeCreatedTimestamps = includeCreatedTimestamps;
21+
this.timestampsInMs = timestampsInMs;
1722
this.exemplarsOnAllMetricTypes = exemplarsOnAllMetricTypes;
1823
}
1924

@@ -22,6 +27,11 @@ public boolean getIncludeCreatedTimestamps() {
2227
return includeCreatedTimestamps != null && includeCreatedTimestamps;
2328
}
2429

30+
/** Use milliseconds for timestamps in text format? Default is {@code false}. */
31+
public boolean getTimestampsInMs() {
32+
return timestampsInMs != null && timestampsInMs;
33+
}
34+
2535
/**
2636
* Allow Exemplars on all metric types in OpenMetrics format? Default is {@code false}, which
2737
* means Exemplars will only be added for Counters and Histogram buckets.
@@ -38,9 +48,12 @@ static ExporterProperties load(Map<Object, Object> properties)
3848
throws PrometheusPropertiesException {
3949
Boolean includeCreatedTimestamps =
4050
Util.loadBoolean(PREFIX + "." + INCLUDE_CREATED_TIMESTAMPS, properties);
51+
Boolean timestampsInMs = Util.loadBoolean(PREFIX + "." + TIMESTAMPS_IN_MS, properties);
4152
Boolean exemplarsOnAllMetricTypes =
4253
Util.loadBoolean(PREFIX + "." + EXEMPLARS_ON_ALL_METRIC_TYPES, properties);
43-
return new ExporterProperties(includeCreatedTimestamps, exemplarsOnAllMetricTypes);
54+
// new prop?
55+
return new ExporterProperties(
56+
includeCreatedTimestamps, timestampsInMs, exemplarsOnAllMetricTypes);
4457
}
4558

4659
public static Builder builder() {
@@ -51,6 +64,7 @@ public static class Builder {
5164

5265
private Boolean includeCreatedTimestamps;
5366
private Boolean exemplarsOnAllMetricTypes;
67+
boolean timestampsInMs;
5468

5569
private Builder() {}
5670

@@ -66,8 +80,15 @@ public Builder exemplarsOnAllMetricTypes(boolean exemplarsOnAllMetricTypes) {
6680
return this;
6781
}
6882

83+
/** See {@link #getTimestampsInMs()}. */
84+
public Builder setTimestampsInMs(boolean timestampsInMs) {
85+
this.timestampsInMs = timestampsInMs;
86+
return this;
87+
}
88+
6989
public ExporterProperties build() {
70-
return new ExporterProperties(includeCreatedTimestamps, exemplarsOnAllMetricTypes);
90+
return new ExporterProperties(
91+
includeCreatedTimestamps, timestampsInMs, exemplarsOnAllMetricTypes);
7192
}
7293
}
7394
}

prometheus-metrics-exporter-pushgateway/src/main/java/io/prometheus/metrics/exporter/pushgateway/PushGateway.java

+27-3
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ public class PushGateway {
8080

8181
private final URL url;
8282
private final ExpositionFormatWriter writer;
83+
private final boolean timestampsInMs;
8384
private final Map<String, String> requestHeaders;
8485
private final PrometheusRegistry registry;
8586
private final HttpConnectionFactory connectionFactory;
@@ -89,20 +90,23 @@ private PushGateway(
8990
Format format,
9091
URL url,
9192
HttpConnectionFactory connectionFactory,
92-
Map<String, String> requestHeaders) {
93+
Map<String, String> requestHeaders,
94+
boolean timestampsInMs) {
9395
this.registry = registry;
9496
this.url = url;
9597
this.requestHeaders = Collections.unmodifiableMap(new HashMap<>(requestHeaders));
9698
this.connectionFactory = connectionFactory;
99+
this.timestampsInMs = timestampsInMs;
97100
writer = getWriter(format);
98101
if (!writer.isAvailable()) {
99102
throw new RuntimeException(writer.getClass() + " is not available");
100103
}
101104
}
102105

106+
@SuppressWarnings("deprecation")
103107
private ExpositionFormatWriter getWriter(Format format) {
104108
if (format == Format.PROMETHEUS_TEXT) {
105-
return new PrometheusTextFormatWriter(false);
109+
return PrometheusTextFormatWriter.builder().setTimestampsInMs(this.timestampsInMs).build();
106110
} else {
107111
// use reflection to avoid a compile-time dependency on the expositionformats module
108112
return new PrometheusProtobufWriter();
@@ -264,6 +268,7 @@ public static class Builder {
264268
private String address;
265269
private Scheme scheme;
266270
private String job;
271+
private boolean timestampsInMs;
267272
private final Map<String, String> requestHeaders = new HashMap<>();
268273
private PrometheusRegistry registry = PrometheusRegistry.defaultRegistry;
269274
private HttpConnectionFactory connectionFactory = new DefaultHttpConnectionFactory();
@@ -380,6 +385,20 @@ public Builder registry(PrometheusRegistry registry) {
380385
return this;
381386
}
382387

388+
/**
389+
* Use milliseconds for timestamps in text format? Default is {@code false}. Can be overwritten
390+
* at runtime with the {@code io.prometheus.exporter.timestampsInMs} property.
391+
*/
392+
public Builder setTimestampsInMs(boolean timestampsInMs) {
393+
this.timestampsInMs = timestampsInMs;
394+
return this;
395+
}
396+
397+
private boolean getTimestampsInMs() {
398+
// accept either to opt in to timestamps in milliseconds
399+
return config.getExporterProperties().getTimestampsInMs() || this.timestampsInMs;
400+
}
401+
383402
private Scheme getScheme(ExporterPushgatewayProperties properties) {
384403
if (properties != null && properties.getScheme() != null) {
385404
return Scheme.valueOf(properties.getScheme());
@@ -453,7 +472,12 @@ public PushGateway build() {
453472
config == null ? null : config.getExporterPushgatewayProperties();
454473
try {
455474
return new PushGateway(
456-
registry, getFormat(), makeUrl(properties), connectionFactory, requestHeaders);
475+
registry,
476+
getFormat(),
477+
makeUrl(properties),
478+
connectionFactory,
479+
requestHeaders,
480+
getTimestampsInMs());
457481
} catch (MalformedURLException e) {
458482
throw new PrometheusPropertiesException(
459483
address + ": Invalid address. Expecting <host>:<port>");

prometheus-metrics-exposition-textformats/src/main/java/io/prometheus/metrics/expositionformats/ExpositionFormats.java

+10-3
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,19 @@ public static ExpositionFormats init() {
2222
return init(PrometheusProperties.get().getExporterProperties());
2323
}
2424

25+
@SuppressWarnings("deprecation")
2526
public static ExpositionFormats init(ExporterProperties properties) {
2627
return new ExpositionFormats(
2728
new PrometheusProtobufWriter(),
28-
new PrometheusTextFormatWriter(properties.getIncludeCreatedTimestamps()),
29-
new OpenMetricsTextFormatWriter(
30-
properties.getIncludeCreatedTimestamps(), properties.getExemplarsOnAllMetricTypes()));
29+
PrometheusTextFormatWriter.builder()
30+
.setIncludeCreatedTimestamps(properties.getIncludeCreatedTimestamps())
31+
.setTimestampsInMs(properties.getTimestampsInMs())
32+
.build(),
33+
OpenMetricsTextFormatWriter.builder()
34+
.setCreatedTimestampsEnabled(properties.getIncludeCreatedTimestamps())
35+
.setExemplarsOnAllMetricTypesEnabled(properties.getExemplarsOnAllMetricTypes())
36+
.setTimestampsInMs(properties.getTimestampsInMs())
37+
.build());
3138
}
3239

3340
public ExpositionFormatWriter findWriter(String acceptHeader) {

prometheus-metrics-exposition-textformats/src/main/java/io/prometheus/metrics/expositionformats/OpenMetricsTextFormatWriter.java

+38-30
Original file line numberDiff line numberDiff line change
@@ -38,68 +38,76 @@
3838
public class OpenMetricsTextFormatWriter implements ExpositionFormatWriter {
3939

4040
public static class Builder {
41-
CreatedTimestampStyle createdTimestampStyle;
41+
boolean createdTimestampsEnabled;
42+
boolean timestampsInMs = true;
4243
boolean exemplarsOnAllMetricTypesEnabled;
4344

44-
private Builder() {
45-
}
45+
private Builder() {}
4646

4747
/**
4848
* @param createdTimestampsEnabled whether to include the _created timestamp in the output
4949
*/
50-
public void setCreatedTimestampsEnabled(boolean createdTimestampsEnabled) {
51-
this.createdTimestampStyle = CreatedTimestampStyle.fromEnabled(createdTimestampsEnabled);
50+
public Builder setCreatedTimestampsEnabled(boolean createdTimestampsEnabled) {
51+
this.createdTimestampsEnabled = createdTimestampsEnabled;
52+
return this;
5253
}
5354

5455
/**
55-
* @param exemplarsOnAllMetricTypesEnabled whether to include exemplars in the output for all metric types
56+
* @param exemplarsOnAllMetricTypesEnabled whether to include exemplars in the output for all
57+
* metric types
5658
*/
57-
public void setExemplarsOnAllMetricTypesEnabled(boolean exemplarsOnAllMetricTypesEnabled) {
59+
public Builder setExemplarsOnAllMetricTypesEnabled(boolean exemplarsOnAllMetricTypesEnabled) {
5860
this.exemplarsOnAllMetricTypesEnabled = exemplarsOnAllMetricTypesEnabled;
61+
return this;
5962
}
60-
}
61-
62-
private enum CreatedTimestampStyle {
63-
DISABLED,
64-
DEPRECATED,
65-
MILLISECONDS;
6663

67-
private static CreatedTimestampStyle fromDeprecated(boolean createdTimestampsEnabled) {
68-
return createdTimestampsEnabled ? DEPRECATED : DISABLED;
64+
@Deprecated
65+
public Builder setTimestampsInMs(boolean timestampsInMs) {
66+
this.timestampsInMs = timestampsInMs;
67+
return this;
6968
}
7069

71-
private static CreatedTimestampStyle fromEnabled(boolean createdTimestampsEnabled) {
72-
return createdTimestampsEnabled ? MILLISECONDS : DISABLED;
70+
public OpenMetricsTextFormatWriter build() {
71+
return new OpenMetricsTextFormatWriter(
72+
createdTimestampsEnabled, timestampsInMs, exemplarsOnAllMetricTypesEnabled);
7373
}
7474
}
7575

7676
public static final String CONTENT_TYPE =
7777
"application/openmetrics-text; version=1.0.0; charset=utf-8";
78-
private final CreatedTimestampStyle createdTimestampStyle;
78+
private final boolean createdTimestampsEnabled;
79+
private final boolean timestampsInMs;
7980
private final boolean exemplarsOnAllMetricTypesEnabled;
8081

8182
/**
82-
* @param createdTimestampsEnabled whether to include the _created timestamp in the output - This will produce an invalid OpenMetrics output, but is kept for backwards compatibility.
83-
* @deprecated this constructor is deprecated and will be removed in the next major version. Use
83+
* @param createdTimestampsEnabled whether to include the _created timestamp in the output - This
84+
* will produce an invalid OpenMetrics output, but is kept for backwards compatibility.
85+
* @deprecated this constructor is deprecated and will be removed in the next major version -
86+
* {@link #builder()} or {@link #create()} instead
8487
*/
8588
@Deprecated
8689
public OpenMetricsTextFormatWriter(
8790
boolean createdTimestampsEnabled, boolean exemplarsOnAllMetricTypesEnabled) {
88-
this(CreatedTimestampStyle.fromDeprecated(createdTimestampsEnabled), exemplarsOnAllMetricTypesEnabled);
91+
this(createdTimestampsEnabled, false, exemplarsOnAllMetricTypesEnabled);
8992
}
9093

91-
// some new ctor that produces milliceconds timestamps as required by the OpenMetrics spec
92-
// https://prometheus.io/docs/instrumenting/exposition_formats/#comments-help-text-and-type-information
9394
private OpenMetricsTextFormatWriter(
94-
CreatedTimestampStyle createdTimestampStyle, boolean exemplarsOnAllMetricTypesEnabled) {
95-
this.createdTimestampStyle = createdTimestampStyle;
95+
boolean createdTimestampsEnabled,
96+
boolean timestampsInMs,
97+
boolean exemplarsOnAllMetricTypesEnabled) {
98+
this.createdTimestampsEnabled = createdTimestampsEnabled;
99+
this.timestampsInMs = timestampsInMs;
96100
this.exemplarsOnAllMetricTypesEnabled = exemplarsOnAllMetricTypesEnabled;
97101
}
98102

99103
public static Builder builder() {
100104
return new Builder();
101105
}
102106

107+
public static OpenMetricsTextFormatWriter create() {
108+
return builder().build();
109+
}
110+
103111
@Override
104112
public boolean accepts(String acceptHeader) {
105113
if (acceptHeader == null) {
@@ -345,12 +353,12 @@ private void writeCountAndSum(
345353

346354
private void writeCreated(Writer writer, MetricMetadata metadata, DataPointSnapshot data)
347355
throws IOException {
348-
if (createdTimestampStyle != CreatedTimestampStyle.DISABLED && data.hasCreatedTimestamp()) {
356+
if (createdTimestampsEnabled && data.hasCreatedTimestamp()) {
349357
writeNameAndLabels(writer, metadata.getPrometheusName(), "_created", data.getLabels());
350-
writeTimestamp(writer, data.getCreatedTimestampMillis());
358+
writeTimestamp(writer, data.getCreatedTimestampMillis(), timestampsInMs);
351359
if (data.hasScrapeTimestamp()) {
352360
writer.write(' ');
353-
writeTimestamp(writer, data.getScrapeTimestampMillis());
361+
writeTimestamp(writer, data.getScrapeTimestampMillis(), timestampsInMs);
354362
}
355363
writer.write('\n');
356364
}
@@ -383,7 +391,7 @@ private void writeScrapeTimestampAndExemplar(
383391
Writer writer, DataPointSnapshot data, Exemplar exemplar) throws IOException {
384392
if (data.hasScrapeTimestamp()) {
385393
writer.write(' ');
386-
writeTimestamp(writer, data.getScrapeTimestampMillis());
394+
writeTimestamp(writer, data.getScrapeTimestampMillis(), timestampsInMs);
387395
}
388396
if (exemplar != null) {
389397
writer.write(" # ");
@@ -392,7 +400,7 @@ private void writeScrapeTimestampAndExemplar(
392400
writeDouble(writer, exemplar.getValue());
393401
if (exemplar.hasTimestamp()) {
394402
writer.write(' ');
395-
writeTimestamp(writer, exemplar.getTimestampMillis());
403+
writeTimestamp(writer, exemplar.getTimestampMillis(), timestampsInMs);
396404
}
397405
}
398406
writer.write('\n');

prometheus-metrics-exposition-textformats/src/main/java/io/prometheus/metrics/expositionformats/PrometheusTextFormatWriter.java

+48-2
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,55 @@ public class PrometheusTextFormatWriter implements ExpositionFormatWriter {
3636
public static final String CONTENT_TYPE = "text/plain; version=0.0.4; charset=utf-8";
3737

3838
private final boolean writeCreatedTimestamps;
39+
private final boolean timestampsInMs;
3940

41+
public static class Builder {
42+
boolean includeCreatedTimestamps;
43+
boolean timestampsInMs = true;
44+
45+
private Builder() {}
46+
47+
/**
48+
* @param includeCreatedTimestamps whether to include the _created timestamp in the output
49+
*/
50+
public Builder setIncludeCreatedTimestamps(boolean includeCreatedTimestamps) {
51+
this.includeCreatedTimestamps = includeCreatedTimestamps;
52+
return this;
53+
}
54+
55+
@Deprecated
56+
public Builder setTimestampsInMs(boolean timestampsInMs) {
57+
this.timestampsInMs = timestampsInMs;
58+
return this;
59+
}
60+
61+
public PrometheusTextFormatWriter build() {
62+
return new PrometheusTextFormatWriter(includeCreatedTimestamps, timestampsInMs);
63+
}
64+
}
65+
66+
/**
67+
* @param writeCreatedTimestamps whether to include the _created timestamp in the output - This
68+
* will produce an invalid OpenMetrics output, but is kept for backwards compatibility.
69+
* @deprecated this constructor is deprecated and will be removed in the next major version -
70+
* {@link #builder()} or {@link #create()} instead
71+
*/
72+
@Deprecated
4073
public PrometheusTextFormatWriter(boolean writeCreatedTimestamps) {
74+
this(writeCreatedTimestamps, false);
75+
}
76+
77+
private PrometheusTextFormatWriter(boolean writeCreatedTimestamps, boolean timestampsInMs) {
4178
this.writeCreatedTimestamps = writeCreatedTimestamps;
79+
this.timestampsInMs = timestampsInMs;
80+
}
81+
82+
public static PrometheusTextFormatWriter.Builder builder() {
83+
return new Builder();
84+
}
85+
86+
public static PrometheusTextFormatWriter create() {
87+
return builder().build();
4288
}
4389

4490
@Override
@@ -106,7 +152,7 @@ public void writeCreated(Writer writer, MetricSnapshot snapshot) throws IOExcept
106152
metadataWritten = true;
107153
}
108154
writeNameAndLabels(writer, metadata.getPrometheusName(), "_created", data.getLabels());
109-
writeTimestamp(writer, data.getCreatedTimestampMillis());
155+
writeTimestamp(writer, data.getCreatedTimestampMillis(), timestampsInMs);
110156
writeScrapeTimestampAndNewline(writer, data);
111157
}
112158
}
@@ -363,7 +409,7 @@ private void writeScrapeTimestampAndNewline(Writer writer, DataPointSnapshot dat
363409
throws IOException {
364410
if (data.hasScrapeTimestamp()) {
365411
writer.write(' ');
366-
writeTimestamp(writer, data.getScrapeTimestampMillis());
412+
writeTimestamp(writer, data.getScrapeTimestampMillis(), timestampsInMs);
367413
}
368414
writer.write('\n');
369415
}

0 commit comments

Comments
 (0)