Skip to content

Commit

Permalink
Extend Counters, Summaries and Histograms with creation timestamp
Browse files Browse the repository at this point in the history
Signed-off-by: Arthur Silva Sens <arthur.sens@coralogix.com>
  • Loading branch information
ArthurSens committed Aug 11, 2023
1 parent 1a88780 commit 59086cc
Show file tree
Hide file tree
Showing 12 changed files with 314 additions and 17 deletions.
2 changes: 1 addition & 1 deletion go.mod
Expand Up @@ -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.42.0
github.com/prometheus/procfs v0.11.1
golang.org/x/sys v0.10.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Expand Up @@ -152,8 +152,8 @@ github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQg
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
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.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM=
github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc=
Expand Down
15 changes: 13 additions & 2 deletions prometheus/counter.go
Expand Up @@ -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
Expand Down Expand Up @@ -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
}

Expand All @@ -106,6 +111,7 @@ type counter struct {
selfCollector
desc *Desc

createdTs *timestamppb.Timestamp
labelPairs []*dto.LabelPair
exemplar atomic.Value // Containing nil or a *dto.Exemplar.

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -200,13 +207,17 @@ 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) {
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels.names, lvs))
}
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
}),
}
Expand Down
37 changes: 37 additions & 0 deletions prometheus/counter_test.go
Expand Up @@ -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())
}
}
2 changes: 1 addition & 1 deletion prometheus/gauge.go
Expand Up @@ -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
Expand Down
20 changes: 16 additions & 4 deletions prometheus/histogram.go
Expand Up @@ -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
Expand Down Expand Up @@ -471,6 +472,8 @@ type HistogramOpts struct {
NativeHistogramMaxBucketNumber uint32
NativeHistogramMinResetDuration time.Duration
NativeHistogramMaxZeroThreshold float64

now func() time.Time // To mock out time.Now() for testing.
}

// HistogramVecOpts bundles the options to create a HistogramVec metric.
Expand All @@ -492,6 +495,9 @@ type HistogramVecOpts struct {
// perform the corresponding type assertion. Exemplars are tracked separately
// for each bucket.
func NewHistogram(opts HistogramOpts) Histogram {
if opts.now == nil {
opts.now = time.Now
}
return newHistogram(
NewDesc(
BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
Expand Down Expand Up @@ -569,6 +575,7 @@ func newHistogram(desc *Desc, opts HistogramOpts, labelValues ...string) Histogr
h.exemplars = make([]atomic.Value, len(h.upperBounds)+1)

h.init(h) // Init self-collection.
h.createdTs = timestamppb.New(opts.now())
return h
}

Expand Down Expand Up @@ -707,6 +714,7 @@ 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.
}
Expand Down Expand Up @@ -747,9 +755,10 @@ func (h *histogram) Write(out *dto.Metric) error {
waitForCooldown(count, coldCounts)

his := &dto.Histogram{
Bucket: make([]*dto.Bucket, len(h.upperBounds)),
SampleCount: proto.Uint64(count),
SampleSum: proto.Float64(math.Float64frombits(atomic.LoadUint64(&coldCounts.sumBits))),
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
Expand Down Expand Up @@ -1194,14 +1203,17 @@ type constHistogram struct {
sum float64
buckets map[float64]uint64
labelPairs []*dto.LabelPair
createdTs *timestamppb.Timestamp
}

func (h *constHistogram) Desc() *Desc {
return h.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))

Expand Down
41 changes: 41 additions & 0 deletions prometheus/histogram_test.go
Expand Up @@ -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())
}
}
2 changes: 2 additions & 0 deletions prometheus/metric.go
Expand Up @@ -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 // To mock out time.Now() for testing.

Check failure on line 96 in prometheus/metric.go

View workflow job for this annotation

GitHub Actions / lint

`now` is unused (structcheck)
}

// BuildFQName joins the given three name components by "_". Empty name
Expand Down
26 changes: 22 additions & 4 deletions prometheus/summary.go
Expand Up @@ -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
Expand Down Expand Up @@ -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 // To mock out time.Now() for testing.
}

// SummaryVecOpts bundles the options to create a SummaryVec metric.
Expand Down Expand Up @@ -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{
Expand All @@ -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
}

Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -286,6 +294,8 @@ type summary struct {
headStream *quantile.Stream
headStreamIdx int
headStreamExpTime, hotBufExpTime time.Time

createdTs *timestamppb.Timestamp
}

func (s *summary) Desc() *Desc {
Expand All @@ -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()
Expand Down Expand Up @@ -440,6 +452,8 @@ type noObjectivesSummary struct {
counts [2]*summaryCounts

labelPairs []*dto.LabelPair

createdTs *timestamppb.Timestamp
}

func (s *noObjectivesSummary) Desc() *Desc {
Expand Down Expand Up @@ -490,8 +504,9 @@ func (s *noObjectivesSummary) Write(out *dto.Metric) error {
}

sum := &dto.Summary{
SampleCount: proto.Uint64(count),
SampleSum: proto.Float64(math.Float64frombits(atomic.LoadUint64(&coldCounts.sumBits))),
SampleCount: proto.Uint64(count),
SampleSum: proto.Float64(math.Float64frombits(atomic.LoadUint64(&coldCounts.sumBits))),
CreatedTimestamp: s.createdTs,
}

out.Summary = sum
Expand Down Expand Up @@ -681,14 +696,17 @@ type constSummary struct {
sum float64
quantiles map[float64]float64
labelPairs []*dto.LabelPair
createdTs *timestamppb.Timestamp
}

func (s *constSummary) Desc() *Desc {
return s.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)
Expand Down

0 comments on commit 59086cc

Please sign in to comment.