Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow creating constant histogram and summary metrics with a created timestamp #1537

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions prometheus/examples_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,35 @@ func ExampleNewConstSummary() {
// {"label":[{"name":"code","value":"200"},{"name":"method","value":"get"},{"name":"owner","value":"example"}],"summary":{"sampleCount":"4711","sampleSum":403.34,"quantile":[{"quantile":0.5,"value":42.3},{"quantile":0.9,"value":323.3}]}}
}

func ExampleNewConstSummaryWithCreatedTimestamp() {
desc := prometheus.NewDesc(
"http_request_duration_seconds",
"A summary of the HTTP request durations.",
[]string{"code", "method"},
prometheus.Labels{"owner": "example"},
)

// Create a constant summary with created timestamp set
createdTs := time.Unix(1719670764, 123)
s := prometheus.MustNewConstSummaryWithCreatedTimestamp(
desc,
4711, 403.34,
map[float64]float64{0.5: 42.3, 0.9: 323.3},
createdTs,
"200", "get",
)

// Just for demonstration, let's check the state of the summary by
// (ab)using its Write method (which is usually only used by Prometheus
// internally).
metric := &dto.Metric{}
s.Write(metric)
fmt.Println(toNormalizedJSON(metric))

// Output:
// {"label":[{"name":"code","value":"200"},{"name":"method","value":"get"},{"name":"owner","value":"example"}],"summary":{"sampleCount":"4711","sampleSum":403.34,"quantile":[{"quantile":0.5,"value":42.3},{"quantile":0.9,"value":323.3}],"createdTimestamp":"2024-06-29T14:19:24.000000123Z"}}
}

func ExampleHistogram() {
temps := prometheus.NewHistogram(prometheus.HistogramOpts{
Name: "pond_temperature_celsius",
Expand Down Expand Up @@ -456,6 +485,34 @@ func ExampleNewConstHistogram() {
// {"label":[{"name":"code","value":"200"},{"name":"method","value":"get"},{"name":"owner","value":"example"}],"histogram":{"sampleCount":"4711","sampleSum":403.34,"bucket":[{"cumulativeCount":"121","upperBound":25},{"cumulativeCount":"2403","upperBound":50},{"cumulativeCount":"3221","upperBound":100},{"cumulativeCount":"4233","upperBound":200}]}}
}

func ExampleNewConstHistogramWithCreatedTimestamp() {
desc := prometheus.NewDesc(
"http_request_duration_seconds",
"A histogram of the HTTP request durations.",
[]string{"code", "method"},
prometheus.Labels{"owner": "example"},
)

createdTs := time.Unix(1719670764, 123)
h := prometheus.MustNewConstHistogramWithCreatedTimestamp(
desc,
4711, 403.34,
map[float64]uint64{25: 121, 50: 2403, 100: 3221, 200: 4233},
createdTs,
"200", "get",
)

// Just for demonstration, let's check the state of the histogram by
// (ab)using its Write method (which is usually only used by Prometheus
// internally).
metric := &dto.Metric{}
h.Write(metric)
fmt.Println(toNormalizedJSON(metric))

// Output:
// {"label":[{"name":"code","value":"200"},{"name":"method","value":"get"},{"name":"owner","value":"example"}],"histogram":{"sampleCount":"4711","sampleSum":403.34,"bucket":[{"cumulativeCount":"121","upperBound":25},{"cumulativeCount":"2403","upperBound":50},{"cumulativeCount":"3221","upperBound":100},{"cumulativeCount":"4233","upperBound":200}],"createdTimestamp":"2024-06-29T14:19:24.000000123Z"}}
}

func ExampleNewConstHistogram_WithExemplar() {
desc := prometheus.NewDesc(
"http_request_duration_seconds",
Expand Down
42 changes: 42 additions & 0 deletions prometheus/histogram.go
Original file line number Diff line number Diff line change
Expand Up @@ -1373,6 +1373,48 @@ func MustNewConstHistogram(
return m
}

// NewConstHistogramWithCreatedTimestamp does the same thing as NewConstHistogram but sets the created timestamp.
func NewConstHistogramWithCreatedTimestamp(
desc *Desc,
count uint64,
sum float64,
buckets map[float64]uint64,
ct time.Time,
labelValues ...string,
) (Metric, error) {
if desc.err != nil {
return nil, desc.err
}
if err := validateLabelValues(labelValues, len(desc.variableLabels.names)); err != nil {
return nil, err
}
return &constHistogram{
desc: desc,
count: count,
sum: sum,
buckets: buckets,
labelPairs: MakeLabelPairs(desc, labelValues),
createdTs: timestamppb.New(ct),
}, nil
}

// MustNewConstHistogramWithCreatedTimestamp is a version of NewConstHistogramWithCreatedTimestamp that panics where
// NewConstHistogramWithCreatedTimestamp would have returned an error.
func MustNewConstHistogramWithCreatedTimestamp(
desc *Desc,
count uint64,
sum float64,
buckets map[float64]uint64,
ct time.Time,
labelValues ...string,
) Metric {
m, err := NewConstHistogramWithCreatedTimestamp(desc, count, sum, buckets, ct, labelValues...)
if err != nil {
panic(err)
}
return m
}

type buckSort []*dto.Bucket

func (s buckSort) Len() int {
Expand Down
25 changes: 25 additions & 0 deletions prometheus/histogram_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1272,6 +1272,31 @@ func TestHistogramVecCreatedTimestampWithDeletes(t *testing.T) {
expectCTsForMetricVecValues(t, histogramVec.MetricVec, dto.MetricType_HISTOGRAM, expected)
}

func TestNewConstHistogramWithCreatedTimestamp(t *testing.T) {
metricDesc := NewDesc(
"sample_value",
"sample value",
nil,
nil,
)
buckets := map[float64]uint64{25: 100, 50: 200}
createdTs := time.Unix(1719670764, 123)

h, err := NewConstHistogramWithCreatedTimestamp(metricDesc, 100, 200, buckets, createdTs)
if err != nil {
t.Fatal(err)
}

var metric dto.Metric
if err := h.Write(&metric); err != nil {
t.Fatal(err)
}

if metric.Histogram.CreatedTimestamp.AsTime().UnixMicro() != createdTs.UnixMicro() {
t.Errorf("Expected created timestamp %v, got %v", createdTs, &metric.Histogram.CreatedTimestamp)
}
}

func TestNativeHistogramExemplar(t *testing.T) {
// Test the histogram with positive NativeHistogramExemplarTTL and NativeHistogramMaxExemplars
h := NewHistogram(HistogramOpts{
Expand Down
42 changes: 42 additions & 0 deletions prometheus/summary.go
Original file line number Diff line number Diff line change
Expand Up @@ -783,3 +783,45 @@ func MustNewConstSummary(
}
return m
}

// NewConstSummaryWithCreatedTimestamp does the same thing as NewConstSummary but sets the created timestamp.
func NewConstSummaryWithCreatedTimestamp(
desc *Desc,
count uint64,
sum float64,
quantiles map[float64]float64,
ct time.Time,
labelValues ...string,
) (Metric, error) {
if desc.err != nil {
return nil, desc.err
}
if err := validateLabelValues(labelValues, len(desc.variableLabels.names)); err != nil {
return nil, err
}
return &constSummary{
desc: desc,
count: count,
sum: sum,
quantiles: quantiles,
labelPairs: MakeLabelPairs(desc, labelValues),
createdTs: timestamppb.New(ct),
}, nil
}

// MustNewConstSummaryWithCreatedTimestamp is a version of NewConstSummaryWithCreatedTimestamp that panics where
// NewConstSummaryWithCreatedTimestamp would have returned an error.
func MustNewConstSummaryWithCreatedTimestamp(
desc *Desc,
count uint64,
sum float64,
quantiles map[float64]float64,
ct time.Time,
labelValues ...string,
) Metric {
m, err := NewConstSummaryWithCreatedTimestamp(desc, count, sum, quantiles, ct, labelValues...)
if err != nil {
panic(err)
}
return m
}
25 changes: 25 additions & 0 deletions prometheus/summary_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -474,3 +474,28 @@ func TestSummaryVecCreatedTimestampWithDeletes(t *testing.T) {
})
}
}

func TestNewConstSummaryWithCreatedTimestamp(t *testing.T) {
metricDesc := NewDesc(
"sample_value",
"sample value",
nil,
nil,
)
quantiles := map[float64]float64{50: 200.12, 99: 500.342}
createdTs := time.Unix(1719670764, 123)

s, err := NewConstSummaryWithCreatedTimestamp(metricDesc, 100, 200, quantiles, createdTs)
if err != nil {
t.Fatal(err)
}

var metric dto.Metric
if err := s.Write(&metric); err != nil {
t.Fatal(err)
}

if metric.Summary.CreatedTimestamp.AsTime().UnixMicro() != createdTs.UnixMicro() {
t.Errorf("Expected created timestamp %v, got %v", createdTs, &metric.Summary.CreatedTimestamp)
}
}
Loading