@@ -17,6 +17,7 @@ import (
17
17
"errors"
18
18
"math"
19
19
"sync/atomic"
20
+ "time"
20
21
21
22
dto "github.com/prometheus/client_model/go"
22
23
)
@@ -40,6 +41,14 @@ type Counter interface {
40
41
// Add adds the given value to the counter. It panics if the value is <
41
42
// 0.
42
43
Add (float64 )
44
+ // AddWithExemplar works like Add but also replaces the currently saved
45
+ // exemplar (if any) with a new one, created from the provided value,
46
+ // the current time as timestamp, and the provided labels. Empty Labels
47
+ // will lead to a valid (label-less) exemplar. But if Labels is nil, the
48
+ // current exemplar is left in place. This method panics if the value is
49
+ // < 0, if any of the provided labels are invalid, or if the provided
50
+ // labels contain more than 64 runes in total.
51
+ AddWithExemplar (value float64 , exemplar Labels )
43
52
}
44
53
45
54
// CounterOpts is an alias for Opts. See there for doc comments.
@@ -61,7 +70,7 @@ func NewCounter(opts CounterOpts) Counter {
61
70
nil ,
62
71
opts .ConstLabels ,
63
72
)
64
- result := & counter {desc : desc , labelPairs : desc .constLabelPairs }
73
+ result := & counter {desc : desc , labelPairs : desc .constLabelPairs , now : time . Now }
65
74
result .init (result ) // Init self-collection.
66
75
return result
67
76
}
@@ -78,6 +87,9 @@ type counter struct {
78
87
desc * Desc
79
88
80
89
labelPairs []* dto.LabelPair
90
+ exemplar atomic.Value // Containing nil or a *dto.Exemplar.
91
+
92
+ now func () time.Time // To mock out time.Now() for testing.
81
93
}
82
94
83
95
func (c * counter ) Desc () * Desc {
@@ -88,6 +100,7 @@ func (c *counter) Add(v float64) {
88
100
if v < 0 {
89
101
panic (errors .New ("counter cannot decrease in value" ))
90
102
}
103
+
91
104
ival := uint64 (v )
92
105
if float64 (ival ) == v {
93
106
atomic .AddUint64 (& c .valInt , ival )
@@ -103,6 +116,11 @@ func (c *counter) Add(v float64) {
103
116
}
104
117
}
105
118
119
+ func (c * counter ) AddWithExemplar (v float64 , e Labels ) {
120
+ c .Add (v )
121
+ c .updateExemplar (v , e )
122
+ }
123
+
106
124
func (c * counter ) Inc () {
107
125
atomic .AddUint64 (& c .valInt , 1 )
108
126
}
@@ -112,7 +130,23 @@ func (c *counter) Write(out *dto.Metric) error {
112
130
ival := atomic .LoadUint64 (& c .valInt )
113
131
val := fval + float64 (ival )
114
132
115
- return populateMetric (CounterValue , val , c .labelPairs , out )
133
+ var exemplar * dto.Exemplar
134
+ if e := c .exemplar .Load (); e != nil {
135
+ exemplar = e .(* dto.Exemplar )
136
+ }
137
+
138
+ return populateMetric (CounterValue , val , c .labelPairs , exemplar , out )
139
+ }
140
+
141
+ func (c * counter ) updateExemplar (v float64 , l Labels ) {
142
+ if l == nil {
143
+ return
144
+ }
145
+ e , err := newExemplar (v , c .now (), l )
146
+ if err != nil {
147
+ panic (err )
148
+ }
149
+ c .exemplar .Store (e )
116
150
}
117
151
118
152
// CounterVec is a Collector that bundles a set of Counters that all share the
0 commit comments