@@ -18,6 +18,7 @@ import (
1818 "fmt"
1919 "sort"
2020 "sync"
21+ "time"
2122
2223 "github.com/cespare/xxhash/v2"
2324 "github.com/golang/protobuf/proto"
@@ -80,30 +81,42 @@ func (s *CollectSession) Commit() {
8081 s .c .pendingSession = false
8182}
8283
83- // NewMetric ...
84+ func (s * CollectSession ) MustAddMetric (fqName , help string , labelNames , labelValues []string , valueType ValueType , value float64 , ts * time.Time ) {
85+ if err := s .AddMetric (fqName , help , labelNames , labelValues , valueType , value , ts ); err != nil {
86+ panic (err )
87+ }
88+ }
89+
90+ // AddMetric ...
8491// TODO(bwplotka): Add validation.
85- func (s * CollectSession ) NewMetric (fqName , help string , labelNames , labelValues []string , valueType ValueType , value float64 ) error {
92+ func (s * CollectSession ) AddMetric (fqName , help string , labelNames , labelValues []string , valueType ValueType , value float64 , ts * time. Time ) error {
8693 if s .closed {
8794 return errors .New ("new metric: collect session is closed, but was attempted to be used" )
8895 }
8996
90- if ! sort .StringsAreSorted (labelNames ) {
91- return errors .New ("new metric: label names has to be sorted" )
92- }
97+ // Label names can be unsorted, will be sorting them later. The only implication is cachability if
98+ // consumer provide non-deterministic order of those (unlikely since label values has to be matched.
9399
94100 if len (labelNames ) != len (labelValues ) {
95101 return errors .New ("new metric: label name has different len than values" )
96102 }
97103
98- d , ok := s .c .metricFamilyByName [fqName ]
104+ d , ok := s .currentByName [fqName ]
105+ if ! ok {
106+ d , ok = s .c .metricFamilyByName [fqName ]
107+ if ok {
108+ d .Metric = d .Metric [:0 ]
109+ }
110+ }
111+
99112 if ! ok {
100113 // TODO(bwplotka): Validate?
101114 d = & dto.MetricFamily {}
102115 d .Name = proto .String (fqName )
103116 d .Type = valueType .ToDTO ()
104117 d .Help = proto .String (help )
105118 } else {
106- // TODO(bwplotka): Validate if same family.
119+ // TODO(bwplotka): Validate if same family?
107120 d .Type = valueType .ToDTO ()
108121 d .Help = proto .String (help )
109122 }
@@ -120,6 +133,9 @@ func (s *CollectSession) NewMetric(fqName, help string, labelNames, labelValues
120133 }
121134 hSum := h .Sum64 ()
122135
136+ if _ , ok := s .currentMetrics [hSum ]; ok {
137+ return fmt .Errorf ("found duplicate metric (same labels and values) to add %v" , fqName )
138+ }
123139 m , ok := s .c .metrics [hSum ]
124140 if ! ok {
125141 m = & dto.Metric {
@@ -166,23 +182,28 @@ func (s *CollectSession) NewMetric(fqName, help string, labelNames, labelValues
166182 return fmt .Errorf ("unsupported value type %v" , valueType )
167183 }
168184
185+ m .TimestampMs = nil
186+ if ts != nil {
187+ m .TimestampMs = proto .Int64 (ts .Unix ()* 1000 + int64 (ts .Nanosecond ()/ 1000000 ))
188+ }
189+
169190 // Will be sorted later.
170191 d .Metric = append (d .Metric , m )
171192 return nil
172193}
173194
174195type BlockingRegistry struct {
175- Gatherer
196+ * Registry
176197
177198 // rawCollector represents special collectors which requires blocking collect for the whole duration
178199 // of returned dto.MetricFamily usage.
179200 rawCollectors []rawCollector
180201 mu sync.Mutex
181202}
182203
183- func NewBlockingRegistry (g Gatherer ) * BlockingRegistry {
204+ func NewBlockingRegistry () * BlockingRegistry {
184205 return & BlockingRegistry {
185- Gatherer : g ,
206+ Registry : NewRegistry () ,
186207 }
187208}
188209
@@ -203,9 +224,9 @@ func (b *BlockingRegistry) MustRegisterRaw(r rawCollector) {
203224}
204225
205226func (b * BlockingRegistry ) Gather () (_ []* dto.MetricFamily , done func (), err error ) {
206- mfs , err := b .Gatherer .Gather ()
207-
208227 b .mu .Lock ()
228+ mfs , err := b .Registry .Gather ()
229+
209230 // TODO(bwplotka): Returned mfs are sorted, so sort raw ones and inject?
210231 // TODO(bwplotka): Implement concurrency for those?
211232 for _ , r := range b .rawCollectors {
@@ -225,3 +246,16 @@ type TransactionalGatherer interface {
225246 // Gather ...
226247 Gather () (_ []* dto.MetricFamily , done func (), err error )
227248}
249+
250+ func ToTransactionalGatherer (g Gatherer ) TransactionalGatherer {
251+ return & noTransactionGatherer {g : g }
252+ }
253+
254+ type noTransactionGatherer struct {
255+ g Gatherer
256+ }
257+
258+ func (g * noTransactionGatherer ) Gather () (_ []* dto.MetricFamily , done func (), err error ) {
259+ mfs , err := g .g .Gather ()
260+ return mfs , func () {}, err
261+ }
0 commit comments