Skip to content

Commit

Permalink
feat: Support Prometheus Created Timestamps (#34596)
Browse files Browse the repository at this point in the history
**Description:** exporter/prometheusexporter now supports Prometheus
Created Timestamps.

**Link to tracking Issue:** Fix #32521 

**Testing:** Unit tests were extended with the new scope

**Documentation:** No documentation was added so far, looking for
direction about where would be the best place for it :)

Signed-off-by: Arthur Silva Sens <arthursens2005@gmail.com>
  • Loading branch information
ArthurSens committed Sep 4, 2024
1 parent d0e5eec commit 5258d98
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 19 deletions.
27 changes: 27 additions & 0 deletions .chloggen/prometheusexporter_createdtimestamp.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Use this changelog template to create an entry for release notes.

# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: enhancement

# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
component: exporter/prometheusexpoter

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Support for Prometheus Created Timestamps.

# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
issues: [32521]

# (Optional) One or more lines of additional information to render under the primary note.
# These lines will be padded with 2 spaces and then inserted directly into the document.
# Use pipe (|) for multiline entries.
subtext:

# If your change doesn't affect end users or the exported elements of any package,
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
# Optional: The change log or logs in which this entry should be included.
# e.g. '[user]' or '[user, api]'
# Include 'user' if the change is relevant to end users.
# Include 'api' if there is a change to a library API.
# Default: '[user]'
change_logs: [user]
24 changes: 21 additions & 3 deletions exporter/prometheusexporter/collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,13 @@ func (c *collector) convertSum(metric pmetric.Metric, resourceAttrs pcommon.Map)
exemplars = convertExemplars(ip.Exemplars())
}

m, err := prometheus.NewConstMetric(desc, metricType, value, attributes...)
var m prometheus.Metric
var err error
if metricType == prometheus.CounterValue && ip.StartTimestamp().AsTime().Unix() > 0 {
m, err = prometheus.NewConstMetricWithCreatedTimestamp(desc, metricType, value, ip.StartTimestamp().AsTime(), attributes...)
} else {
m, err = prometheus.NewConstMetric(desc, metricType, value, attributes...)
}
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -214,7 +220,13 @@ func (c *collector) convertSummary(metric pmetric.Metric, resourceAttrs pcommon.
}

desc, attributes := c.getMetricMetadata(metric, point.Attributes(), resourceAttrs)
m, err := prometheus.NewConstSummary(desc, point.Count(), point.Sum(), quantiles, attributes...)
var m prometheus.Metric
var err error
if point.StartTimestamp().AsTime().Unix() > 0 {
m, err = prometheus.NewConstSummaryWithCreatedTimestamp(desc, point.Count(), point.Sum(), quantiles, point.StartTimestamp().AsTime(), attributes...)
} else {
m, err = prometheus.NewConstSummary(desc, point.Count(), point.Sum(), quantiles, attributes...)
}
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -254,7 +266,13 @@ func (c *collector) convertDoubleHistogram(metric pmetric.Metric, resourceAttrs

exemplars := convertExemplars(ip.Exemplars())

m, err := prometheus.NewConstHistogram(desc, ip.Count(), ip.Sum(), points, attributes...)
var m prometheus.Metric
var err error
if ip.StartTimestamp().AsTime().Unix() > 0 {
m, err = prometheus.NewConstHistogramWithCreatedTimestamp(desc, ip.Count(), ip.Sum(), points, ip.StartTimestamp().AsTime(), attributes...)
} else {
m, err = prometheus.NewConstHistogram(desc, ip.Count(), ip.Sum(), points, attributes...)
}
if err != nil {
return nil, err
}
Expand Down
75 changes: 60 additions & 15 deletions exporter/prometheusexporter/collector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
conventions "go.opentelemetry.io/collector/semconv/v1.25.0"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"google.golang.org/protobuf/types/known/timestamppb"

prometheustranslator "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheus"
)
Expand Down Expand Up @@ -287,15 +288,15 @@ func TestCollectMetricsLabelSanitize(t *testing.T) {
func TestCollectMetrics(t *testing.T) {
tests := []struct {
name string
metric func(time.Time) pmetric.Metric
metric func(time.Time, bool) pmetric.Metric
metricType prometheus.ValueType
value float64
}{
{
name: "IntGauge",
metricType: prometheus.GaugeValue,
value: 42.0,
metric: func(ts time.Time) (metric pmetric.Metric) {
metric: func(ts time.Time, withStartTime bool) (metric pmetric.Metric) {
metric = pmetric.NewMetric()
metric.SetName("test_metric")
metric.SetDescription("test description")
Expand All @@ -304,6 +305,9 @@ func TestCollectMetrics(t *testing.T) {
dp.Attributes().PutStr("label_1", "1")
dp.Attributes().PutStr("label_2", "2")
dp.SetTimestamp(pcommon.NewTimestampFromTime(ts))
if withStartTime {
dp.SetStartTimestamp(pcommon.NewTimestampFromTime(ts))
}

return
},
Expand All @@ -312,7 +316,7 @@ func TestCollectMetrics(t *testing.T) {
name: "Gauge",
metricType: prometheus.GaugeValue,
value: 42.42,
metric: func(ts time.Time) (metric pmetric.Metric) {
metric: func(ts time.Time, withStartTime bool) (metric pmetric.Metric) {
metric = pmetric.NewMetric()
metric.SetName("test_metric")
metric.SetDescription("test description")
Expand All @@ -321,6 +325,9 @@ func TestCollectMetrics(t *testing.T) {
dp.Attributes().PutStr("label_1", "1")
dp.Attributes().PutStr("label_2", "2")
dp.SetTimestamp(pcommon.NewTimestampFromTime(ts))
if withStartTime {
dp.SetStartTimestamp(pcommon.NewTimestampFromTime(ts))
}

return
},
Expand All @@ -329,7 +336,7 @@ func TestCollectMetrics(t *testing.T) {
name: "IntSum",
metricType: prometheus.GaugeValue,
value: 42.0,
metric: func(ts time.Time) (metric pmetric.Metric) {
metric: func(ts time.Time, withStartTime bool) (metric pmetric.Metric) {
metric = pmetric.NewMetric()
metric.SetName("test_metric")
metric.SetEmptySum().SetIsMonotonic(false)
Expand All @@ -340,6 +347,9 @@ func TestCollectMetrics(t *testing.T) {
dp.Attributes().PutStr("label_1", "1")
dp.Attributes().PutStr("label_2", "2")
dp.SetTimestamp(pcommon.NewTimestampFromTime(ts))
if withStartTime {
dp.SetStartTimestamp(pcommon.NewTimestampFromTime(ts))
}

return
},
Expand All @@ -348,7 +358,7 @@ func TestCollectMetrics(t *testing.T) {
name: "Sum",
metricType: prometheus.GaugeValue,
value: 42.42,
metric: func(ts time.Time) (metric pmetric.Metric) {
metric: func(ts time.Time, withStartTime bool) (metric pmetric.Metric) {
metric = pmetric.NewMetric()
metric.SetName("test_metric")
metric.SetEmptySum().SetIsMonotonic(false)
Expand All @@ -359,6 +369,9 @@ func TestCollectMetrics(t *testing.T) {
dp.Attributes().PutStr("label_1", "1")
dp.Attributes().PutStr("label_2", "2")
dp.SetTimestamp(pcommon.NewTimestampFromTime(ts))
if withStartTime {
dp.SetStartTimestamp(pcommon.NewTimestampFromTime(ts))
}

return
},
Expand All @@ -367,7 +380,7 @@ func TestCollectMetrics(t *testing.T) {
name: "MonotonicIntSum",
metricType: prometheus.CounterValue,
value: 42.0,
metric: func(ts time.Time) (metric pmetric.Metric) {
metric: func(ts time.Time, withStartTime bool) (metric pmetric.Metric) {
metric = pmetric.NewMetric()
metric.SetName("test_metric")
metric.SetEmptySum().SetIsMonotonic(true)
Expand All @@ -378,6 +391,9 @@ func TestCollectMetrics(t *testing.T) {
dp.Attributes().PutStr("label_1", "1")
dp.Attributes().PutStr("label_2", "2")
dp.SetTimestamp(pcommon.NewTimestampFromTime(ts))
if withStartTime {
dp.SetStartTimestamp(pcommon.NewTimestampFromTime(ts))
}

return
},
Expand All @@ -386,7 +402,7 @@ func TestCollectMetrics(t *testing.T) {
name: "MonotonicSum",
metricType: prometheus.CounterValue,
value: 42.42,
metric: func(ts time.Time) (metric pmetric.Metric) {
metric: func(ts time.Time, withStartTime bool) (metric pmetric.Metric) {
metric = pmetric.NewMetric()
metric.SetName("test_metric")
metric.SetEmptySum().SetIsMonotonic(true)
Expand All @@ -397,6 +413,9 @@ func TestCollectMetrics(t *testing.T) {
dp.Attributes().PutStr("label_1", "1")
dp.Attributes().PutStr("label_2", "2")
dp.SetTimestamp(pcommon.NewTimestampFromTime(ts))
if withStartTime {
dp.SetStartTimestamp(pcommon.NewTimestampFromTime(ts))
}

return
},
Expand All @@ -405,7 +424,7 @@ func TestCollectMetrics(t *testing.T) {
name: "Unknown",
metricType: prometheus.UntypedValue,
value: 42.42,
metric: func(ts time.Time) (metric pmetric.Metric) {
metric: func(ts time.Time, withStartTime bool) (metric pmetric.Metric) {
metric = pmetric.NewMetric()
metric.SetName("test_metric")
metric.SetDescription("test description")
Expand All @@ -415,6 +434,9 @@ func TestCollectMetrics(t *testing.T) {
dp.Attributes().PutStr("label_1", "1")
dp.Attributes().PutStr("label_2", "2")
dp.SetTimestamp(pcommon.NewTimestampFromTime(ts))
if withStartTime {
dp.SetStartTimestamp(pcommon.NewTimestampFromTime(ts))
}

return
},
Expand All @@ -424,6 +446,8 @@ func TestCollectMetrics(t *testing.T) {
for _, tt := range tests {
for _, sendTimestamp := range []bool{true, false} {
name := tt.name
// In this test, sendTimestamp is used to test
// both prometheus regular timestamp and "created timestamp".
if sendTimestamp {
name += "/WithTimestamp"
}
Expand All @@ -435,7 +459,7 @@ func TestCollectMetrics(t *testing.T) {

t.Run(name, func(t *testing.T) {
ts := time.Now()
metric := tt.metric(ts)
metric := tt.metric(ts, sendTimestamp)
c := collector{
namespace: "test_space",
accumulator: &mockAccumulator{
Expand Down Expand Up @@ -481,8 +505,15 @@ func TestCollectMetrics(t *testing.T) {

if sendTimestamp {
require.Equal(t, ts.UnixNano()/1e6, *(pbMetric.TimestampMs))
// Prometheus gauges don't have created timestamp.
if tt.metricType == prometheus.CounterValue {
require.Equal(t, timestamppb.New(ts), pbMetric.Counter.CreatedTimestamp)
}
} else {
require.Nil(t, pbMetric.TimestampMs)
if tt.metricType == prometheus.CounterValue {
require.Nil(t, pbMetric.Counter.CreatedTimestamp)
}
}

switch tt.metricType {
Expand All @@ -507,7 +538,7 @@ func TestCollectMetrics(t *testing.T) {
func TestAccumulateHistograms(t *testing.T) {
tests := []struct {
name string
metric func(time.Time) pmetric.Metric
metric func(time.Time, bool) pmetric.Metric

histogramPoints map[float64]uint64
histogramSum float64
Expand All @@ -521,7 +552,7 @@ func TestAccumulateHistograms(t *testing.T) {
},
histogramSum: 42.42,
histogramCount: 7,
metric: func(ts time.Time) (metric pmetric.Metric) {
metric: func(ts time.Time, withStartTime bool) (metric pmetric.Metric) {
metric = pmetric.NewMetric()
metric.SetName("test_metric")
metric.SetEmptyHistogram().SetAggregationTemporality(pmetric.AggregationTemporalityCumulative)
Expand All @@ -534,6 +565,9 @@ func TestAccumulateHistograms(t *testing.T) {
dp.Attributes().PutStr("label_1", "1")
dp.Attributes().PutStr("label_2", "2")
dp.SetTimestamp(pcommon.NewTimestampFromTime(ts))
if withStartTime {
dp.SetStartTimestamp(pcommon.NewTimestampFromTime(ts))
}
return
},
},
Expand All @@ -542,12 +576,14 @@ func TestAccumulateHistograms(t *testing.T) {
for _, tt := range tests {
for _, sendTimestamp := range []bool{true, false} {
name := tt.name
// In this test, sendTimestamp is used to test
// both prometheus regular timestamp and "created timestamp".
if sendTimestamp {
name += "/WithTimestamp"
}
t.Run(name, func(t *testing.T) {
ts := time.Now()
metric := tt.metric(ts)
metric := tt.metric(ts, sendTimestamp)
c := collector{
accumulator: &mockAccumulator{
[]pmetric.Metric{metric},
Expand Down Expand Up @@ -580,8 +616,10 @@ func TestAccumulateHistograms(t *testing.T) {

if sendTimestamp {
require.Equal(t, ts.UnixNano()/1e6, *(pbMetric.TimestampMs))
require.Equal(t, timestamppb.New(ts), pbMetric.Histogram.CreatedTimestamp)
} else {
require.Nil(t, pbMetric.TimestampMs)
require.Nil(t, pbMetric.Histogram.CreatedTimestamp)
}

require.Nil(t, pbMetric.Gauge)
Expand Down Expand Up @@ -609,7 +647,7 @@ func TestAccumulateSummary(t *testing.T) {
}
tests := []struct {
name string
metric func(time.Time) pmetric.Metric
metric func(time.Time, bool) pmetric.Metric
wantSum float64
wantCount uint64
wantQuantiles map[float64]float64
Expand All @@ -622,7 +660,7 @@ func TestAccumulateSummary(t *testing.T) {
0.50: 190,
0.99: 817,
},
metric: func(ts time.Time) (metric pmetric.Metric) {
metric: func(ts time.Time, withStartTime bool) (metric pmetric.Metric) {
metric = pmetric.NewMetric()
metric.SetName("test_metric")
metric.SetDescription("test description")
Expand All @@ -633,6 +671,9 @@ func TestAccumulateSummary(t *testing.T) {
sp.Attributes().PutStr("label_1", "1")
sp.Attributes().PutStr("label_2", "2")
sp.SetTimestamp(pcommon.NewTimestampFromTime(ts))
if withStartTime {
sp.SetStartTimestamp(pcommon.NewTimestampFromTime(ts))
}

fillQuantileValue(0.50, 190, sp.QuantileValues().AppendEmpty())
fillQuantileValue(0.99, 817, sp.QuantileValues().AppendEmpty())
Expand All @@ -645,12 +686,14 @@ func TestAccumulateSummary(t *testing.T) {
for _, tt := range tests {
for _, sendTimestamp := range []bool{true, false} {
name := tt.name
// In this test, sendTimestamp is used to test
// both prometheus regular timestamp and "created timestamp".
if sendTimestamp {
name += "/WithTimestamp"
}
t.Run(name, func(t *testing.T) {
ts := time.Now()
metric := tt.metric(ts)
metric := tt.metric(ts, sendTimestamp)
c := collector{
accumulator: &mockAccumulator{
[]pmetric.Metric{metric},
Expand Down Expand Up @@ -683,8 +726,10 @@ func TestAccumulateSummary(t *testing.T) {

if sendTimestamp {
require.Equal(t, ts.UnixNano()/1e6, *(pbMetric.TimestampMs))
require.Equal(t, timestamppb.New(ts), pbMetric.Summary.CreatedTimestamp)
} else {
require.Nil(t, pbMetric.TimestampMs)
require.Nil(t, pbMetric.Summary.CreatedTimestamp)
}

require.Nil(t, pbMetric.Gauge)
Expand Down
2 changes: 1 addition & 1 deletion exporter/prometheusexporter/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ require (
go.opentelemetry.io/collector/semconv v0.108.2-0.20240829190554-7da6b618a7ee
go.uber.org/goleak v1.3.0
go.uber.org/zap v1.27.0
google.golang.org/protobuf v1.34.2
gopkg.in/yaml.v2 v2.4.0
)

Expand Down Expand Up @@ -178,7 +179,6 @@ require (
google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd // indirect
google.golang.org/grpc v1.65.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
Expand Down

0 comments on commit 5258d98

Please sign in to comment.