Extend Counters, Summaries and Histograms with creation timestamp
Signed-off-by: Arthur Silva Sens <arthur.sens@coralogix.com>
This commit is contained in:
parent
51d24f8680
commit
3c7e78cf3e
2
go.mod
2
go.mod
|
@ -7,7 +7,7 @@ require (
|
|||
github.com/cespare/xxhash/v2 v2.2.0
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/json-iterator/go v1.1.12
|
||||
github.com/prometheus/client_model v0.4.0
|
||||
github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16
|
||||
github.com/prometheus/common v0.44.0
|
||||
github.com/prometheus/procfs v0.11.1
|
||||
golang.org/x/sys v0.11.0
|
||||
|
|
4
go.sum
4
go.sum
|
@ -34,8 +34,8 @@ github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRW
|
|||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY=
|
||||
github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
|
||||
github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 h1:v7DLqVdK4VrYkVD5diGdl4sxJurKJEMnODWRJlxV9oM=
|
||||
github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
|
||||
github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY=
|
||||
github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY=
|
||||
github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI=
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
"time"
|
||||
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
// Counter is a Metric that represents a single numerical value that only ever
|
||||
|
@ -90,8 +91,12 @@ func NewCounter(opts CounterOpts) Counter {
|
|||
nil,
|
||||
opts.ConstLabels,
|
||||
)
|
||||
result := &counter{desc: desc, labelPairs: desc.constLabelPairs, now: time.Now}
|
||||
if opts.now == nil {
|
||||
opts.now = time.Now
|
||||
}
|
||||
result := &counter{desc: desc, labelPairs: desc.constLabelPairs, now: opts.now}
|
||||
result.init(result) // Init self-collection.
|
||||
result.createdTs = timestamppb.New(opts.now())
|
||||
return result
|
||||
}
|
||||
|
||||
|
@ -106,10 +111,11 @@ type counter struct {
|
|||
selfCollector
|
||||
desc *Desc
|
||||
|
||||
createdTs *timestamppb.Timestamp
|
||||
labelPairs []*dto.LabelPair
|
||||
exemplar atomic.Value // Containing nil or a *dto.Exemplar.
|
||||
|
||||
now func() time.Time // To mock out time.Now() for testing.
|
||||
now func() time.Time // For testing, all constructors put time.Now() here.
|
||||
}
|
||||
|
||||
func (c *counter) Desc() *Desc {
|
||||
|
@ -160,7 +166,8 @@ func (c *counter) Write(out *dto.Metric) error {
|
|||
}
|
||||
val := c.get()
|
||||
|
||||
return populateMetric(CounterValue, val, c.labelPairs, exemplar, out)
|
||||
ct := c.createdTs.AsTime()
|
||||
return populateMetric(CounterValue, val, c.labelPairs, exemplar, out, &ct)
|
||||
}
|
||||
|
||||
func (c *counter) updateExemplar(v float64, l Labels) {
|
||||
|
@ -200,6 +207,9 @@ func (v2) NewCounterVec(opts CounterVecOpts) *CounterVec {
|
|||
opts.VariableLabels,
|
||||
opts.ConstLabels,
|
||||
)
|
||||
if opts.now == nil {
|
||||
opts.now = time.Now
|
||||
}
|
||||
return &CounterVec{
|
||||
MetricVec: NewMetricVec(desc, func(lvs ...string) Metric {
|
||||
if len(lvs) != len(desc.variableLabels.names) {
|
||||
|
@ -207,6 +217,7 @@ func (v2) NewCounterVec(opts CounterVecOpts) *CounterVec {
|
|||
}
|
||||
result := &counter{desc: desc, labelPairs: MakeLabelPairs(desc, lvs), now: time.Now}
|
||||
result.init(result) // Init self-collection.
|
||||
result.createdTs = timestamppb.New(opts.now())
|
||||
return result
|
||||
}),
|
||||
}
|
||||
|
|
|
@ -298,3 +298,40 @@ func TestCounterExemplar(t *testing.T) {
|
|||
t.Error("adding exemplar with oversized labels succeeded")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCounterCreatedTimestamp(t *testing.T) {
|
||||
now := time.Now()
|
||||
counter := NewCounter(CounterOpts{
|
||||
Name: "test",
|
||||
Help: "test help",
|
||||
now: func() time.Time { return now },
|
||||
})
|
||||
|
||||
var metric dto.Metric
|
||||
if err := counter.Write(&metric); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if metric.Counter.CreatedTimestamp.AsTime().Unix() != now.Unix() {
|
||||
t.Errorf("expected created timestamp %d, got %d", now.Unix(), metric.Counter.CreatedTimestamp.AsTime().Unix())
|
||||
}
|
||||
}
|
||||
|
||||
func TestCounterVecCreatedTimestamp(t *testing.T) {
|
||||
now := time.Now()
|
||||
counterVec := NewCounterVec(CounterOpts{
|
||||
Name: "test",
|
||||
Help: "test help",
|
||||
now: func() time.Time { return now },
|
||||
}, []string{"label"})
|
||||
counter := counterVec.WithLabelValues("value")
|
||||
|
||||
var metric dto.Metric
|
||||
if err := counter.Write(&metric); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if metric.Counter.CreatedTimestamp.AsTime().Unix() != now.Unix() {
|
||||
t.Errorf("expected created timestamp %d, got %d", now.Unix(), metric.Counter.CreatedTimestamp.AsTime().Unix())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -135,7 +135,7 @@ func (g *gauge) Sub(val float64) {
|
|||
|
||||
func (g *gauge) Write(out *dto.Metric) error {
|
||||
val := math.Float64frombits(atomic.LoadUint64(&g.valBits))
|
||||
return populateMetric(GaugeValue, val, g.labelPairs, nil, out)
|
||||
return populateMetric(GaugeValue, val, g.labelPairs, nil, out, nil)
|
||||
}
|
||||
|
||||
// GaugeVec is a Collector that bundles a set of Gauges that all share the same
|
||||
|
|
|
@ -25,6 +25,7 @@ import (
|
|||
dto "github.com/prometheus/client_model/go"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
// nativeHistogramBounds for the frac of observed values. Only relevant for
|
||||
|
@ -471,6 +472,8 @@ type HistogramOpts struct {
|
|||
NativeHistogramMaxBucketNumber uint32
|
||||
NativeHistogramMinResetDuration time.Duration
|
||||
NativeHistogramMaxZeroThreshold float64
|
||||
|
||||
now func() time.Time // For testing, all constructors put time.Now() here.
|
||||
}
|
||||
|
||||
// HistogramVecOpts bundles the options to create a HistogramVec metric.
|
||||
|
@ -568,7 +571,11 @@ func newHistogram(desc *Desc, opts HistogramOpts, labelValues ...string) Histogr
|
|||
atomic.StoreInt32(&h.counts[1].nativeHistogramSchema, h.nativeHistogramSchema)
|
||||
h.exemplars = make([]atomic.Value, len(h.upperBounds)+1)
|
||||
|
||||
if opts.now == nil {
|
||||
opts.now = time.Now
|
||||
}
|
||||
h.init(h) // Init self-collection.
|
||||
h.createdTs = timestamppb.New(opts.now())
|
||||
return h
|
||||
}
|
||||
|
||||
|
@ -707,8 +714,9 @@ type histogram struct {
|
|||
nativeHistogramMaxBuckets uint32
|
||||
nativeHistogramMinResetDuration time.Duration
|
||||
lastResetTime time.Time // Protected by mtx.
|
||||
createdTs *timestamppb.Timestamp
|
||||
|
||||
now func() time.Time // To mock out time.Now() for testing.
|
||||
now func() time.Time // For testing, all constructors put time.Now() here.
|
||||
}
|
||||
|
||||
func (h *histogram) Desc() *Desc {
|
||||
|
@ -750,6 +758,7 @@ func (h *histogram) Write(out *dto.Metric) error {
|
|||
Bucket: make([]*dto.Bucket, len(h.upperBounds)),
|
||||
SampleCount: proto.Uint64(count),
|
||||
SampleSum: proto.Float64(math.Float64frombits(atomic.LoadUint64(&coldCounts.sumBits))),
|
||||
CreatedTimestamp: h.createdTs,
|
||||
}
|
||||
out.Histogram = his
|
||||
out.Label = h.labelPairs
|
||||
|
@ -1194,6 +1203,7 @@ type constHistogram struct {
|
|||
sum float64
|
||||
buckets map[float64]uint64
|
||||
labelPairs []*dto.LabelPair
|
||||
createdTs *timestamppb.Timestamp
|
||||
}
|
||||
|
||||
func (h *constHistogram) Desc() *Desc {
|
||||
|
@ -1201,7 +1211,9 @@ func (h *constHistogram) Desc() *Desc {
|
|||
}
|
||||
|
||||
func (h *constHistogram) Write(out *dto.Metric) error {
|
||||
his := &dto.Histogram{}
|
||||
his := &dto.Histogram{
|
||||
CreatedTimestamp: h.createdTs,
|
||||
}
|
||||
|
||||
buckets := make([]*dto.Bucket, 0, len(h.buckets))
|
||||
|
||||
|
|
|
@ -1152,3 +1152,44 @@ func TestGetLe(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHistogramCreatedTimestamp(t *testing.T) {
|
||||
now := time.Now()
|
||||
|
||||
histogram := NewHistogram(HistogramOpts{
|
||||
Name: "test",
|
||||
Help: "test help",
|
||||
Buckets: []float64{1, 2, 3, 4},
|
||||
now: func() time.Time { return now },
|
||||
})
|
||||
|
||||
var metric dto.Metric
|
||||
if err := histogram.Write(&metric); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if metric.Histogram.CreatedTimestamp.AsTime().Unix() != now.Unix() {
|
||||
t.Errorf("expected created timestamp %d, got %d", now.Unix(), metric.Histogram.CreatedTimestamp.AsTime().Unix())
|
||||
}
|
||||
}
|
||||
|
||||
func TestHistogramVecCreatedTimestamp(t *testing.T) {
|
||||
now := time.Now()
|
||||
|
||||
histogramVec := NewHistogramVec(HistogramOpts{
|
||||
Name: "test",
|
||||
Help: "test help",
|
||||
Buckets: []float64{1, 2, 3, 4},
|
||||
now: func() time.Time { return now },
|
||||
}, []string{"label"})
|
||||
histogram := histogramVec.WithLabelValues("value").(Histogram)
|
||||
|
||||
var metric dto.Metric
|
||||
if err := histogram.Write(&metric); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if metric.Histogram.CreatedTimestamp.AsTime().Unix() != now.Unix() {
|
||||
t.Errorf("expected created timestamp %d, got %d", now.Unix(), metric.Histogram.CreatedTimestamp.AsTime().Unix())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -92,6 +92,8 @@ type Opts struct {
|
|||
// machine_role metric). See also
|
||||
// https://prometheus.io/docs/instrumenting/writing_exporters/#target-labels-not-static-scraped-labels
|
||||
ConstLabels Labels
|
||||
|
||||
now func() time.Time // For testing, all constructors put time.Now() here.
|
||||
}
|
||||
|
||||
// BuildFQName joins the given three name components by "_". Empty name
|
||||
|
|
|
@ -26,6 +26,7 @@ import (
|
|||
|
||||
"github.com/beorn7/perks/quantile"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
// quantileLabel is used for the label that defines the quantile in a
|
||||
|
@ -145,6 +146,8 @@ type SummaryOpts struct {
|
|||
// is the internal buffer size of the underlying package
|
||||
// "github.com/bmizerany/perks/quantile").
|
||||
BufCap uint32
|
||||
|
||||
now func() time.Time // For testing, all constructors put time.Now() here.
|
||||
}
|
||||
|
||||
// SummaryVecOpts bundles the options to create a SummaryVec metric.
|
||||
|
@ -222,6 +225,9 @@ func newSummary(desc *Desc, opts SummaryOpts, labelValues ...string) Summary {
|
|||
opts.BufCap = DefBufCap
|
||||
}
|
||||
|
||||
if opts.now == nil {
|
||||
opts.now = time.Now
|
||||
}
|
||||
if len(opts.Objectives) == 0 {
|
||||
// Use the lock-free implementation of a Summary without objectives.
|
||||
s := &noObjectivesSummary{
|
||||
|
@ -230,6 +236,7 @@ func newSummary(desc *Desc, opts SummaryOpts, labelValues ...string) Summary {
|
|||
counts: [2]*summaryCounts{{}, {}},
|
||||
}
|
||||
s.init(s) // Init self-collection.
|
||||
s.createdTs = timestamppb.New(opts.now())
|
||||
return s
|
||||
}
|
||||
|
||||
|
@ -259,6 +266,7 @@ func newSummary(desc *Desc, opts SummaryOpts, labelValues ...string) Summary {
|
|||
sort.Float64s(s.sortedObjectives)
|
||||
|
||||
s.init(s) // Init self-collection.
|
||||
s.createdTs = timestamppb.New(opts.now())
|
||||
return s
|
||||
}
|
||||
|
||||
|
@ -286,6 +294,8 @@ type summary struct {
|
|||
headStream *quantile.Stream
|
||||
headStreamIdx int
|
||||
headStreamExpTime, hotBufExpTime time.Time
|
||||
|
||||
createdTs *timestamppb.Timestamp
|
||||
}
|
||||
|
||||
func (s *summary) Desc() *Desc {
|
||||
|
@ -307,7 +317,9 @@ func (s *summary) Observe(v float64) {
|
|||
}
|
||||
|
||||
func (s *summary) Write(out *dto.Metric) error {
|
||||
sum := &dto.Summary{}
|
||||
sum := &dto.Summary{
|
||||
CreatedTimestamp: s.createdTs,
|
||||
}
|
||||
qs := make([]*dto.Quantile, 0, len(s.objectives))
|
||||
|
||||
s.bufMtx.Lock()
|
||||
|
@ -440,6 +452,8 @@ type noObjectivesSummary struct {
|
|||
counts [2]*summaryCounts
|
||||
|
||||
labelPairs []*dto.LabelPair
|
||||
|
||||
createdTs *timestamppb.Timestamp
|
||||
}
|
||||
|
||||
func (s *noObjectivesSummary) Desc() *Desc {
|
||||
|
@ -492,6 +506,7 @@ func (s *noObjectivesSummary) Write(out *dto.Metric) error {
|
|||
sum := &dto.Summary{
|
||||
SampleCount: proto.Uint64(count),
|
||||
SampleSum: proto.Float64(math.Float64frombits(atomic.LoadUint64(&coldCounts.sumBits))),
|
||||
CreatedTimestamp: s.createdTs,
|
||||
}
|
||||
|
||||
out.Summary = sum
|
||||
|
@ -681,6 +696,7 @@ type constSummary struct {
|
|||
sum float64
|
||||
quantiles map[float64]float64
|
||||
labelPairs []*dto.LabelPair
|
||||
createdTs *timestamppb.Timestamp
|
||||
}
|
||||
|
||||
func (s *constSummary) Desc() *Desc {
|
||||
|
@ -688,7 +704,9 @@ func (s *constSummary) Desc() *Desc {
|
|||
}
|
||||
|
||||
func (s *constSummary) Write(out *dto.Metric) error {
|
||||
sum := &dto.Summary{}
|
||||
sum := &dto.Summary{
|
||||
CreatedTimestamp: s.createdTs,
|
||||
}
|
||||
qs := make([]*dto.Quantile, 0, len(s.quantiles))
|
||||
|
||||
sum.SampleCount = proto.Uint64(s.count)
|
||||
|
|
|
@ -420,3 +420,78 @@ func getBounds(vars []float64, q, ε float64) (min, max float64) {
|
|||
}
|
||||
return
|
||||
}
|
||||
|
||||
func TestSummaryCreatedTimestamp(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
objectives map[float64]float64
|
||||
}{
|
||||
{
|
||||
desc: "summary with objectives",
|
||||
objectives: map[float64]float64{
|
||||
1.0: 1.0,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "no objectives summary",
|
||||
objectives: nil,
|
||||
},
|
||||
}
|
||||
now := time.Now()
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
summary := NewSummary(SummaryOpts{
|
||||
Name: "test",
|
||||
Help: "test help",
|
||||
Objectives: test.objectives,
|
||||
now: func() time.Time { return now },
|
||||
})
|
||||
|
||||
var metric dto.Metric
|
||||
summary.Write(&metric)
|
||||
|
||||
if metric.Summary.CreatedTimestamp.AsTime().Unix() != now.Unix() {
|
||||
t.Errorf("expected created timestamp %d, got %d", now.Unix(), metric.Summary.CreatedTimestamp.AsTime().Unix())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSummaryVecCreatedTimestamp(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
objectives map[float64]float64
|
||||
}{
|
||||
{
|
||||
desc: "summary with objectives",
|
||||
objectives: map[float64]float64{
|
||||
1.0: 1.0,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "no objectives summary",
|
||||
objectives: nil,
|
||||
},
|
||||
}
|
||||
now := time.Now()
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
summaryVec := NewSummaryVec(SummaryOpts{
|
||||
Name: "test",
|
||||
Help: "test help",
|
||||
Objectives: test.objectives,
|
||||
now: func() time.Time { return now },
|
||||
},
|
||||
[]string{"label"})
|
||||
summary := summaryVec.WithLabelValues("value").(Summary)
|
||||
var metric dto.Metric
|
||||
summary.Write(&metric)
|
||||
|
||||
if metric.Summary.CreatedTimestamp.AsTime().Unix() != now.Unix() {
|
||||
t.Errorf("expected created timestamp %d, got %d", now.Unix(), metric.Summary.CreatedTimestamp.AsTime().Unix())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
package prometheus
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
|
@ -91,7 +92,7 @@ func (v *valueFunc) Desc() *Desc {
|
|||
}
|
||||
|
||||
func (v *valueFunc) Write(out *dto.Metric) error {
|
||||
return populateMetric(v.valType, v.function(), v.labelPairs, nil, out)
|
||||
return populateMetric(v.valType, v.function(), v.labelPairs, nil, out, nil)
|
||||
}
|
||||
|
||||
// NewConstMetric returns a metric with one fixed value that cannot be
|
||||
|
@ -110,7 +111,7 @@ func NewConstMetric(desc *Desc, valueType ValueType, value float64, labelValues
|
|||
}
|
||||
|
||||
metric := &dto.Metric{}
|
||||
if err := populateMetric(valueType, value, MakeLabelPairs(desc, labelValues), nil, metric); err != nil {
|
||||
if err := populateMetric(valueType, value, MakeLabelPairs(desc, labelValues), nil, metric, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -130,6 +131,43 @@ func MustNewConstMetric(desc *Desc, valueType ValueType, value float64, labelVal
|
|||
return m
|
||||
}
|
||||
|
||||
// NewConstMetricWithCreatedTimestamp does the same thing as NewConstMetric, but generates Counters
|
||||
// with created timestamp set and returns an error for other metric types.
|
||||
func NewConstMetricWithCreatedTimestamp(desc *Desc, valueType ValueType, value 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
|
||||
}
|
||||
switch valueType {
|
||||
case CounterValue:
|
||||
break
|
||||
default:
|
||||
return nil, errors.New("Created timestamps are only supported for counters")
|
||||
}
|
||||
|
||||
metric := &dto.Metric{}
|
||||
if err := populateMetric(valueType, value, MakeLabelPairs(desc, labelValues), nil, metric, &ct); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &constMetric{
|
||||
desc: desc,
|
||||
metric: metric,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// MustNewConstMetricWithCreatedTimestamp is a version of NewConstMetricWithCreatedTimestamp that panics where
|
||||
// NewConstMetricWithCreatedTimestamp would have returned an error.
|
||||
func MustNewConstMetricWithCreatedTimestamp(desc *Desc, valueType ValueType, value float64, ct time.Time, labelValues ...string) Metric {
|
||||
m, err := NewConstMetricWithCreatedTimestamp(desc, valueType, value, ct, labelValues...)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
type constMetric struct {
|
||||
desc *Desc
|
||||
metric *dto.Metric
|
||||
|
@ -153,11 +191,16 @@ func populateMetric(
|
|||
labelPairs []*dto.LabelPair,
|
||||
e *dto.Exemplar,
|
||||
m *dto.Metric,
|
||||
createdTimestamp *time.Time,
|
||||
) error {
|
||||
m.Label = labelPairs
|
||||
switch t {
|
||||
case CounterValue:
|
||||
m.Counter = &dto.Counter{Value: proto.Float64(v), Exemplar: e}
|
||||
var ct *timestamppb.Timestamp
|
||||
if createdTimestamp != nil {
|
||||
ct = timestamppb.New(*createdTimestamp)
|
||||
}
|
||||
m.Counter = &dto.Counter{Value: proto.Float64(v), Exemplar: e, CreatedTimestamp: ct}
|
||||
case GaugeValue:
|
||||
m.Gauge = &dto.Gauge{Value: proto.Float64(v)}
|
||||
case UntypedValue:
|
||||
|
|
|
@ -16,6 +16,10 @@ package prometheus
|
|||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
func TestNewConstMetricInvalidLabelValues(t *testing.T) {
|
||||
|
@ -54,3 +58,56 @@ func TestNewConstMetricInvalidLabelValues(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewConstMetricWithCreatedTimestamp(t *testing.T) {
|
||||
now := time.Now()
|
||||
testCases := []struct {
|
||||
desc string
|
||||
metricType ValueType
|
||||
createdTimestamp time.Time
|
||||
expecErr bool
|
||||
expectedCt *timestamppb.Timestamp
|
||||
}{
|
||||
{
|
||||
desc: "gauge with CT",
|
||||
metricType: GaugeValue,
|
||||
createdTimestamp: now,
|
||||
expecErr: true,
|
||||
expectedCt: nil,
|
||||
},
|
||||
{
|
||||
desc: "counter with CT",
|
||||
metricType: CounterValue,
|
||||
createdTimestamp: now,
|
||||
expecErr: false,
|
||||
expectedCt: timestamppb.New(now),
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
metricDesc := NewDesc(
|
||||
"sample_value",
|
||||
"sample value",
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
m, err := NewConstMetricWithCreatedTimestamp(metricDesc, test.metricType, float64(1), test.createdTimestamp)
|
||||
if test.expecErr && err == nil {
|
||||
t.Errorf("Expected error is test %s, got no err", test.desc)
|
||||
}
|
||||
if !test.expecErr && err != nil {
|
||||
t.Errorf("Didn't expect error in test %s, got %s", test.desc, err.Error())
|
||||
}
|
||||
|
||||
if test.expectedCt != nil {
|
||||
var metric dto.Metric
|
||||
m.Write(&metric)
|
||||
if metric.Counter.CreatedTimestamp.AsTime() != test.expectedCt.AsTime() {
|
||||
t.Errorf("Expected timestamp %v, got %v", test.expectedCt, &metric.Counter.CreatedTimestamp)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue