| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | // Copyright 2019, OpenTelemetry Authors | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // Licensed under the Apache License, Version 2.0 (the "License"); | 
					
						
							|  |  |  | // you may not use this file except in compliance with the License. | 
					
						
							|  |  |  | // You may obtain a copy of the License at | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | //     http://www.apache.org/licenses/LICENSE-2.0 | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // Unless required by applicable law or agreed to in writing, software | 
					
						
							|  |  |  | // distributed under the License is distributed on an "AS IS" BASIS, | 
					
						
							|  |  |  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
					
						
							|  |  |  | // See the License for the specific language governing permissions and | 
					
						
							|  |  |  | // limitations under the License. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package metric | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2019-11-15 13:01:20 -08:00
										 |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"os" | 
					
						
							| 
									
										
										
										
											2020-03-11 09:11:27 -07:00
										 |  |  | 	"reflect" | 
					
						
							| 
									
										
										
										
											2020-02-10 16:20:29 -08:00
										 |  |  | 	"runtime" | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | 	"sort" | 
					
						
							|  |  |  | 	"sync" | 
					
						
							|  |  |  | 	"sync/atomic" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-01 11:40:29 -07:00
										 |  |  | 	"go.opentelemetry.io/otel/api/core" | 
					
						
							|  |  |  | 	"go.opentelemetry.io/otel/api/metric" | 
					
						
							|  |  |  | 	api "go.opentelemetry.io/otel/api/metric" | 
					
						
							| 
									
										
										
										
											2019-11-05 13:08:55 -08:00
										 |  |  | 	export "go.opentelemetry.io/otel/sdk/export/metric" | 
					
						
							| 
									
										
										
										
											2019-11-15 13:01:20 -08:00
										 |  |  | 	"go.opentelemetry.io/otel/sdk/export/metric/aggregator" | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type ( | 
					
						
							|  |  |  | 	// SDK implements the OpenTelemetry Meter API.  The SDK is | 
					
						
							| 
									
										
										
										
											2019-11-15 13:01:20 -08:00
										 |  |  | 	// bound to a single export.Batcher in `New()`. | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | 	// | 
					
						
							|  |  |  | 	// The SDK supports a Collect() API to gather and export | 
					
						
							|  |  |  | 	// current data.  Collect() should be arranged according to | 
					
						
							| 
									
										
										
										
											2019-11-15 13:01:20 -08:00
										 |  |  | 	// the batcher model.  Push-based batchers will setup a | 
					
						
							|  |  |  | 	// timer to call Collect() periodically.  Pull-based batchers | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | 	// will call Collect() when a pull request arrives. | 
					
						
							|  |  |  | 	SDK struct { | 
					
						
							|  |  |  | 		// current maps `mapkey` to *record. | 
					
						
							|  |  |  | 		current sync.Map | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-05 12:15:30 -08:00
										 |  |  | 		// observers is a set of `*observer` instances | 
					
						
							|  |  |  | 		observers sync.Map | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | 		// empty is the (singleton) result of Labels() | 
					
						
							|  |  |  | 		// w/ zero arguments. | 
					
						
							|  |  |  | 		empty labels | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// currentEpoch is the current epoch number. It is | 
					
						
							|  |  |  | 		// incremented in `Collect()`. | 
					
						
							|  |  |  | 		currentEpoch int64 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-15 13:01:20 -08:00
										 |  |  | 		// batcher is the configured batcher+configuration. | 
					
						
							|  |  |  | 		batcher export.Batcher | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// lencoder determines how labels are uniquely encoded. | 
					
						
							|  |  |  | 		labelEncoder export.LabelEncoder | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// collectLock prevents simultaneous calls to Collect(). | 
					
						
							|  |  |  | 		collectLock sync.Mutex | 
					
						
							| 
									
										
										
										
											2019-11-15 13:01:20 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// errorHandler supports delivering errors to the user. | 
					
						
							|  |  |  | 		errorHandler ErrorHandler | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	instrument struct { | 
					
						
							|  |  |  | 		descriptor *export.Descriptor | 
					
						
							|  |  |  | 		meter      *SDK | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-11 09:11:27 -07:00
										 |  |  | 	// orderedLabels is a variable-size array of core.KeyValue | 
					
						
							|  |  |  | 	// suitable for use as a map key. | 
					
						
							|  |  |  | 	orderedLabels interface{} | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// labels implements the OpenTelemetry LabelSet API, | 
					
						
							|  |  |  | 	// represents an internalized set of labels that may be used | 
					
						
							|  |  |  | 	// repeatedly. | 
					
						
							|  |  |  | 	labels struct { | 
					
						
							| 
									
										
										
										
											2020-03-11 09:11:27 -07:00
										 |  |  | 		meter *SDK | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// slice is a slice of `ordered`. | 
					
						
							|  |  |  | 		slice sortedLabels | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// ordered is the output of sorting and deduplicating | 
					
						
							|  |  |  | 		// the labels, copied into an array of the correct | 
					
						
							|  |  |  | 		// size for use as a map key. | 
					
						
							|  |  |  | 		ordered orderedLabels | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// mapkey uniquely describes a metric instrument in terms of | 
					
						
							|  |  |  | 	// its InstrumentID and the encoded form of its LabelSet. | 
					
						
							|  |  |  | 	mapkey struct { | 
					
						
							|  |  |  | 		descriptor *export.Descriptor | 
					
						
							| 
									
										
										
										
											2020-03-11 09:11:27 -07:00
										 |  |  | 		ordered    orderedLabels | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// record maintains the state of one metric instrument.  Due | 
					
						
							|  |  |  | 	// the use of lock-free algorithms, there may be more than one | 
					
						
							|  |  |  | 	// `record` in existence at a time, although at most one can | 
					
						
							|  |  |  | 	// be referenced from the `SDK.current` map. | 
					
						
							|  |  |  | 	record struct { | 
					
						
							| 
									
										
										
										
											2020-02-06 14:45:56 -08:00
										 |  |  | 		// refMapped keeps track of refcounts and the mapping state to the | 
					
						
							|  |  |  | 		// SDK.current map. | 
					
						
							|  |  |  | 		refMapped refcountMapped | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-06 14:45:56 -08:00
										 |  |  | 		// modified is an atomic boolean that tracks if the current record | 
					
						
							|  |  |  | 		// was modified since the last Collect(). | 
					
						
							| 
									
										
										
										
											2020-01-06 10:08:40 -08:00
										 |  |  | 		// | 
					
						
							| 
									
										
										
										
											2020-02-06 14:45:56 -08:00
										 |  |  | 		// modified has to be aligned for 64-bit atomic operations. | 
					
						
							|  |  |  | 		modified int64 | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-06 10:08:40 -08:00
										 |  |  | 		// labels is the LabelSet passed by the user. | 
					
						
							|  |  |  | 		labels *labels | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// descriptor describes the metric instrument. | 
					
						
							|  |  |  | 		descriptor *export.Descriptor | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | 		// recorder implements the actual RecordOne() API, | 
					
						
							|  |  |  | 		// depending on the type of aggregation.  If nil, the | 
					
						
							|  |  |  | 		// metric was disabled by the exporter. | 
					
						
							| 
									
										
										
										
											2019-11-05 13:08:55 -08:00
										 |  |  | 		recorder export.Aggregator | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-05 12:15:30 -08:00
										 |  |  | 	observerResult struct { | 
					
						
							|  |  |  | 		observer *observer | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	int64ObserverResult struct { | 
					
						
							|  |  |  | 		result observerResult | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	float64ObserverResult struct { | 
					
						
							|  |  |  | 		result observerResult | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	observerCallback func(result observerResult) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	observer struct { | 
					
						
							|  |  |  | 		meter      *SDK | 
					
						
							|  |  |  | 		descriptor *export.Descriptor | 
					
						
							| 
									
										
										
										
											2020-03-11 09:11:27 -07:00
										 |  |  | 		// recorders maps ordered labels to the pair of | 
					
						
							| 
									
										
										
										
											2020-03-05 12:15:30 -08:00
										 |  |  | 		// labelset and recorder | 
					
						
							| 
									
										
										
										
											2020-03-11 09:11:27 -07:00
										 |  |  | 		recorders map[orderedLabels]labeledRecorder | 
					
						
							| 
									
										
										
										
											2020-03-05 12:15:30 -08:00
										 |  |  | 		callback  observerCallback | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	labeledRecorder struct { | 
					
						
							|  |  |  | 		recorder      export.Aggregator | 
					
						
							|  |  |  | 		labels        *labels | 
					
						
							|  |  |  | 		modifiedEpoch int64 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	int64Observer struct { | 
					
						
							|  |  |  | 		observer *observer | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	float64Observer struct { | 
					
						
							|  |  |  | 		observer *observer | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-15 13:01:20 -08:00
										 |  |  | 	ErrorHandler func(error) | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var ( | 
					
						
							| 
									
										
										
										
											2020-03-05 12:15:30 -08:00
										 |  |  | 	_ api.Meter                 = &SDK{} | 
					
						
							|  |  |  | 	_ api.LabelSet              = &labels{} | 
					
						
							|  |  |  | 	_ api.InstrumentImpl        = &instrument{} | 
					
						
							|  |  |  | 	_ api.BoundInstrumentImpl   = &record{} | 
					
						
							|  |  |  | 	_ api.Int64Observer         = int64Observer{} | 
					
						
							|  |  |  | 	_ api.Float64Observer       = float64Observer{} | 
					
						
							|  |  |  | 	_ api.Int64ObserverResult   = int64ObserverResult{} | 
					
						
							|  |  |  | 	_ api.Float64ObserverResult = float64ObserverResult{} | 
					
						
							| 
									
										
										
										
											2020-03-11 09:11:27 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	kvType = reflect.TypeOf(core.KeyValue{}) | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-05 12:15:30 -08:00
										 |  |  | func (r observerResult) observe(number core.Number, ls api.LabelSet) { | 
					
						
							|  |  |  | 	r.observer.recordOne(number, ls) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (o *observer) recordOne(number core.Number, ls api.LabelSet) { | 
					
						
							|  |  |  | 	if err := aggregator.RangeTest(number, o.descriptor); err != nil { | 
					
						
							|  |  |  | 		o.meter.errorHandler(err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	recorder := o.getRecorder(ls) | 
					
						
							|  |  |  | 	if recorder == nil { | 
					
						
							|  |  |  | 		// The instrument is disabled according to the | 
					
						
							|  |  |  | 		// AggregationSelector. | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if err := recorder.Update(context.Background(), number, o.descriptor); err != nil { | 
					
						
							|  |  |  | 		o.meter.errorHandler(err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (o *observer) getRecorder(ls api.LabelSet) export.Aggregator { | 
					
						
							|  |  |  | 	labels := o.meter.labsFor(ls) | 
					
						
							| 
									
										
										
										
											2020-03-11 09:11:27 -07:00
										 |  |  | 	lrec, ok := o.recorders[labels.ordered] | 
					
						
							| 
									
										
										
										
											2020-03-05 12:15:30 -08:00
										 |  |  | 	if ok { | 
					
						
							|  |  |  | 		lrec.modifiedEpoch = o.meter.currentEpoch | 
					
						
							| 
									
										
										
										
											2020-03-11 09:11:27 -07:00
										 |  |  | 		o.recorders[labels.ordered] = lrec | 
					
						
							| 
									
										
										
										
											2020-03-05 12:15:30 -08:00
										 |  |  | 		return lrec.recorder | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	rec := o.meter.batcher.AggregatorFor(o.descriptor) | 
					
						
							|  |  |  | 	if o.recorders == nil { | 
					
						
							| 
									
										
										
										
											2020-03-11 09:11:27 -07:00
										 |  |  | 		o.recorders = make(map[orderedLabels]labeledRecorder) | 
					
						
							| 
									
										
										
										
											2020-03-05 12:15:30 -08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	// This may store nil recorder in the map, thus disabling the | 
					
						
							|  |  |  | 	// observer for the labelset for good. This is intentional, | 
					
						
							|  |  |  | 	// but will be revisited later. | 
					
						
							| 
									
										
										
										
											2020-03-11 09:11:27 -07:00
										 |  |  | 	o.recorders[labels.ordered] = labeledRecorder{ | 
					
						
							| 
									
										
										
										
											2020-03-05 12:15:30 -08:00
										 |  |  | 		recorder:      rec, | 
					
						
							|  |  |  | 		labels:        labels, | 
					
						
							|  |  |  | 		modifiedEpoch: o.meter.currentEpoch, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return rec | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (o *observer) unregister() { | 
					
						
							|  |  |  | 	o.meter.observers.Delete(o) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (r int64ObserverResult) Observe(value int64, labels api.LabelSet) { | 
					
						
							|  |  |  | 	r.result.observe(core.NewInt64Number(value), labels) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (r float64ObserverResult) Observe(value float64, labels api.LabelSet) { | 
					
						
							|  |  |  | 	r.result.observe(core.NewFloat64Number(value), labels) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (o int64Observer) Unregister() { | 
					
						
							|  |  |  | 	o.observer.unregister() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (o float64Observer) Unregister() { | 
					
						
							|  |  |  | 	o.observer.unregister() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | func (i *instrument) Meter() api.Meter { | 
					
						
							|  |  |  | 	return i.meter | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-15 13:01:20 -08:00
										 |  |  | func (m *SDK) SetErrorHandler(f ErrorHandler) { | 
					
						
							|  |  |  | 	m.errorHandler = f | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | func (i *instrument) acquireHandle(ls *labels) *record { | 
					
						
							| 
									
										
										
										
											2019-11-06 10:54:36 -08:00
										 |  |  | 	// Create lookup key for sync.Map (one allocation) | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | 	mk := mapkey{ | 
					
						
							|  |  |  | 		descriptor: i.descriptor, | 
					
						
							| 
									
										
										
										
											2020-03-11 09:11:27 -07:00
										 |  |  | 		ordered:    ls.ordered, | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-06 10:54:36 -08:00
										 |  |  | 	if actual, ok := i.meter.current.Load(mk); ok { | 
					
						
							|  |  |  | 		// Existing record case, only one allocation so far. | 
					
						
							|  |  |  | 		rec := actual.(*record) | 
					
						
							| 
									
										
										
										
											2020-02-06 14:45:56 -08:00
										 |  |  | 		if rec.refMapped.ref() { | 
					
						
							|  |  |  | 			// At this moment it is guaranteed that the entry is in | 
					
						
							|  |  |  | 			// the map and will not be removed. | 
					
						
							|  |  |  | 			return rec | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// This entry is no longer mapped, try to add a new entry. | 
					
						
							| 
									
										
										
										
											2019-11-06 10:54:36 -08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | 	// There's a memory allocation here. | 
					
						
							|  |  |  | 	rec := &record{ | 
					
						
							| 
									
										
										
										
											2020-02-06 14:45:56 -08:00
										 |  |  | 		labels:     ls, | 
					
						
							|  |  |  | 		descriptor: i.descriptor, | 
					
						
							|  |  |  | 		refMapped:  refcountMapped{value: 2}, | 
					
						
							|  |  |  | 		modified:   0, | 
					
						
							|  |  |  | 		recorder:   i.meter.batcher.AggregatorFor(i.descriptor), | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-06 14:45:56 -08:00
										 |  |  | 	for { | 
					
						
							|  |  |  | 		// Load/Store: there's a memory allocation to place `mk` into | 
					
						
							|  |  |  | 		// an interface here. | 
					
						
							|  |  |  | 		if actual, loaded := i.meter.current.LoadOrStore(mk, rec); loaded { | 
					
						
							|  |  |  | 			// Existing record case. Cannot change rec here because if fail | 
					
						
							|  |  |  | 			// will try to add rec again to avoid new allocations. | 
					
						
							|  |  |  | 			oldRec := actual.(*record) | 
					
						
							|  |  |  | 			if oldRec.refMapped.ref() { | 
					
						
							|  |  |  | 				// At this moment it is guaranteed that the entry is in | 
					
						
							|  |  |  | 				// the map and will not be removed. | 
					
						
							|  |  |  | 				return oldRec | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			// This loaded entry is marked as unmapped (so Collect will remove | 
					
						
							|  |  |  | 			// it from the map immediately), try again - this is a busy waiting | 
					
						
							|  |  |  | 			// strategy to wait until Collect() removes this entry from the map. | 
					
						
							|  |  |  | 			// | 
					
						
							|  |  |  | 			// This can be improved by having a list of "Unmapped" entries for | 
					
						
							|  |  |  | 			// one time only usages, OR we can make this a blocking path and use | 
					
						
							|  |  |  | 			// a Mutex that protects the delete operation (delete only if the old | 
					
						
							|  |  |  | 			// record is associated with the key). | 
					
						
							| 
									
										
										
										
											2020-02-10 16:20:29 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			// Let collector get work done to remove the entry from the map. | 
					
						
							|  |  |  | 			runtime.Gosched() | 
					
						
							| 
									
										
										
										
											2020-02-06 14:45:56 -08:00
										 |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// The new entry was added to the map, good to go. | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | 		return rec | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-27 16:30:19 -08:00
										 |  |  | func (i *instrument) Bind(ls api.LabelSet) api.BoundInstrumentImpl { | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | 	labs := i.meter.labsFor(ls) | 
					
						
							|  |  |  | 	return i.acquireHandle(labs) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (i *instrument) RecordOne(ctx context.Context, number core.Number, ls api.LabelSet) { | 
					
						
							|  |  |  | 	ourLs := i.meter.labsFor(ls) | 
					
						
							|  |  |  | 	h := i.acquireHandle(ourLs) | 
					
						
							| 
									
										
										
										
											2019-12-27 16:30:19 -08:00
										 |  |  | 	defer h.Unbind() | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | 	h.RecordOne(ctx, number) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-15 13:01:20 -08:00
										 |  |  | // New constructs a new SDK for the given batcher.  This SDK supports | 
					
						
							|  |  |  | // only a single batcher. | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | // | 
					
						
							|  |  |  | // The SDK does not start any background process to collect itself | 
					
						
							| 
									
										
										
										
											2019-11-15 13:01:20 -08:00
										 |  |  | // periodically, this responsbility lies with the batcher, typically, | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | // depending on the type of export.  For example, a pull-based | 
					
						
							| 
									
										
										
										
											2019-11-15 13:01:20 -08:00
										 |  |  | // batcher will call Collect() when it receives a request to scrape | 
					
						
							|  |  |  | // current metric values.  A push-based batcher should configure its | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | // own periodic collection. | 
					
						
							| 
									
										
										
										
											2019-11-15 13:01:20 -08:00
										 |  |  | func New(batcher export.Batcher, labelEncoder export.LabelEncoder) *SDK { | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | 	m := &SDK{ | 
					
						
							| 
									
										
										
										
											2019-11-15 13:01:20 -08:00
										 |  |  | 		batcher:      batcher, | 
					
						
							|  |  |  | 		labelEncoder: labelEncoder, | 
					
						
							|  |  |  | 		errorHandler: DefaultErrorHandler, | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	m.empty.meter = m | 
					
						
							|  |  |  | 	return m | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-15 13:01:20 -08:00
										 |  |  | func DefaultErrorHandler(err error) { | 
					
						
							|  |  |  | 	fmt.Fprintln(os.Stderr, "Metrics SDK error:", err) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | // Labels returns a LabelSet corresponding to the arguments.  Passed | 
					
						
							|  |  |  | // labels are de-duplicated, with last-value-wins semantics. | 
					
						
							|  |  |  | func (m *SDK) Labels(kvs ...core.KeyValue) api.LabelSet { | 
					
						
							|  |  |  | 	// Check for empty set. | 
					
						
							|  |  |  | 	if len(kvs) == 0 { | 
					
						
							|  |  |  | 		return &m.empty | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-11 09:11:27 -07:00
										 |  |  | 	ls := &labels{ // allocation | 
					
						
							|  |  |  | 		meter: m, | 
					
						
							|  |  |  | 		slice: kvs, | 
					
						
							| 
									
										
										
										
											2019-11-14 13:13:42 -08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-11 09:11:27 -07:00
										 |  |  | 	// Sort and de-duplicate.  Note: this use of `ls.slice` avoids | 
					
						
							|  |  |  | 	// an allocation by using the address-able field rather than | 
					
						
							|  |  |  | 	// `kvs`.  Labels retains a copy of this slice, i.e., the | 
					
						
							|  |  |  | 	// initial allocation at the varargs call site. | 
					
						
							|  |  |  | 	// | 
					
						
							|  |  |  | 	// Note that `ls.slice` continues to refer to this memory, | 
					
						
							|  |  |  | 	// even though a new array is allocated for `ls.ordered`.  It | 
					
						
							|  |  |  | 	// is possible for the `slice` to refer to the same memory, | 
					
						
							|  |  |  | 	// although in the reflection code path of `computeOrdered` it | 
					
						
							|  |  |  | 	// costs an allocation to yield a slice through | 
					
						
							|  |  |  | 	// `(reflect.Value).Interface()`. | 
					
						
							|  |  |  | 	// | 
					
						
							|  |  |  | 	// TODO: There is a possibility that the caller passes values | 
					
						
							|  |  |  | 	// without an allocation (e.g., `meter.Labels(kvs...)`), and | 
					
						
							|  |  |  | 	// that the user could later modify the slice, leading to | 
					
						
							|  |  |  | 	// incorrect results.  This is indeed a risk, one that should | 
					
						
							|  |  |  | 	// be quickly addressed via the following TODO. | 
					
						
							|  |  |  | 	// | 
					
						
							|  |  |  | 	// TODO: It would be better overall if the export.Labels interface | 
					
						
							|  |  |  | 	// did not expose a slice via `Ordered()`, if instead it exposed | 
					
						
							|  |  |  | 	// getter methods like `Len()` and `Order(i int)`.  Then we would | 
					
						
							|  |  |  | 	// just implement the interface using the `orderedLabels` array. | 
					
						
							|  |  |  | 	sort.Stable(&ls.slice) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | 	oi := 1 | 
					
						
							| 
									
										
										
										
											2020-03-11 09:11:27 -07:00
										 |  |  | 	for i := 1; i < len(kvs); i++ { | 
					
						
							|  |  |  | 		if kvs[i-1].Key == kvs[i].Key { | 
					
						
							|  |  |  | 			// Overwrite the value for "last-value wins". | 
					
						
							|  |  |  | 			kvs[oi-1].Value = kvs[i].Value | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-03-11 09:11:27 -07:00
										 |  |  | 		kvs[oi] = kvs[i] | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | 		oi++ | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-03-11 09:11:27 -07:00
										 |  |  | 	kvs = kvs[0:oi] | 
					
						
							|  |  |  | 	ls.slice = kvs | 
					
						
							|  |  |  | 	ls.computeOrdered(kvs) | 
					
						
							|  |  |  | 	return ls | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-11 09:11:27 -07:00
										 |  |  | func (ls *labels) computeOrdered(kvs []core.KeyValue) { | 
					
						
							|  |  |  | 	switch len(kvs) { | 
					
						
							|  |  |  | 	case 1: | 
					
						
							|  |  |  | 		ptr := new([1]core.KeyValue) | 
					
						
							|  |  |  | 		copy((*ptr)[:], kvs) | 
					
						
							|  |  |  | 		ls.ordered = *ptr | 
					
						
							|  |  |  | 	case 2: | 
					
						
							|  |  |  | 		ptr := new([2]core.KeyValue) | 
					
						
							|  |  |  | 		copy((*ptr)[:], kvs) | 
					
						
							|  |  |  | 		ls.ordered = *ptr | 
					
						
							|  |  |  | 	case 3: | 
					
						
							|  |  |  | 		ptr := new([3]core.KeyValue) | 
					
						
							|  |  |  | 		copy((*ptr)[:], kvs) | 
					
						
							|  |  |  | 		ls.ordered = *ptr | 
					
						
							|  |  |  | 	case 4: | 
					
						
							|  |  |  | 		ptr := new([4]core.KeyValue) | 
					
						
							|  |  |  | 		copy((*ptr)[:], kvs) | 
					
						
							|  |  |  | 		ls.ordered = *ptr | 
					
						
							|  |  |  | 	case 5: | 
					
						
							|  |  |  | 		ptr := new([5]core.KeyValue) | 
					
						
							|  |  |  | 		copy((*ptr)[:], kvs) | 
					
						
							|  |  |  | 		ls.ordered = *ptr | 
					
						
							|  |  |  | 	case 6: | 
					
						
							|  |  |  | 		ptr := new([6]core.KeyValue) | 
					
						
							|  |  |  | 		copy((*ptr)[:], kvs) | 
					
						
							|  |  |  | 		ls.ordered = *ptr | 
					
						
							|  |  |  | 	case 7: | 
					
						
							|  |  |  | 		ptr := new([7]core.KeyValue) | 
					
						
							|  |  |  | 		copy((*ptr)[:], kvs) | 
					
						
							|  |  |  | 		ls.ordered = *ptr | 
					
						
							|  |  |  | 	case 8: | 
					
						
							|  |  |  | 		ptr := new([8]core.KeyValue) | 
					
						
							|  |  |  | 		copy((*ptr)[:], kvs) | 
					
						
							|  |  |  | 		ls.ordered = *ptr | 
					
						
							|  |  |  | 	case 9: | 
					
						
							|  |  |  | 		ptr := new([9]core.KeyValue) | 
					
						
							|  |  |  | 		copy((*ptr)[:], kvs) | 
					
						
							|  |  |  | 		ls.ordered = *ptr | 
					
						
							|  |  |  | 	case 10: | 
					
						
							|  |  |  | 		ptr := new([10]core.KeyValue) | 
					
						
							|  |  |  | 		copy((*ptr)[:], kvs) | 
					
						
							|  |  |  | 		ls.ordered = *ptr | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		at := reflect.New(reflect.ArrayOf(len(kvs), kvType)).Elem() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		for i := 0; i < len(kvs); i++ { | 
					
						
							|  |  |  | 			*(at.Index(i).Addr().Interface().(*core.KeyValue)) = kvs[i] | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-11-14 13:13:42 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-11 09:11:27 -07:00
										 |  |  | 		ls.ordered = at.Interface() | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // labsFor sanitizes the input LabelSet.  The input will be rejected | 
					
						
							|  |  |  | // if it was created by another Meter instance, for example. | 
					
						
							|  |  |  | func (m *SDK) labsFor(ls api.LabelSet) *labels { | 
					
						
							| 
									
										
										
										
											2019-12-23 23:03:04 -08:00
										 |  |  | 	if del, ok := ls.(api.LabelSetDelegate); ok { | 
					
						
							|  |  |  | 		ls = del.Delegate() | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | 	if l, _ := ls.(*labels); l != nil && l.meter == m { | 
					
						
							|  |  |  | 		return l | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return &m.empty | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-11 20:21:34 -07:00
										 |  |  | func newDescriptor(name string, metricKind export.Kind, numberKind core.NumberKind, opts []api.Option) *export.Descriptor { | 
					
						
							|  |  |  | 	config := api.Configure(opts) | 
					
						
							| 
									
										
										
										
											2020-03-05 12:15:30 -08:00
										 |  |  | 	return export.NewDescriptor( | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | 		name, | 
					
						
							|  |  |  | 		metricKind, | 
					
						
							| 
									
										
										
										
											2020-03-11 20:21:34 -07:00
										 |  |  | 		config.Keys, | 
					
						
							|  |  |  | 		config.Description, | 
					
						
							|  |  |  | 		config.Unit, | 
					
						
							|  |  |  | 		numberKind) | 
					
						
							| 
									
										
										
										
											2020-03-05 12:15:30 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-11 20:21:34 -07:00
										 |  |  | func (m *SDK) newInstrument(name string, metricKind export.Kind, numberKind core.NumberKind, opts []api.Option) *instrument { | 
					
						
							| 
									
										
										
										
											2020-03-05 12:15:30 -08:00
										 |  |  | 	descriptor := newDescriptor(name, metricKind, numberKind, opts) | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | 	return &instrument{ | 
					
						
							|  |  |  | 		descriptor: descriptor, | 
					
						
							|  |  |  | 		meter:      m, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-11 20:21:34 -07:00
										 |  |  | func (m *SDK) newCounterInstrument(name string, numberKind core.NumberKind, opts []api.Option) *instrument { | 
					
						
							|  |  |  | 	return m.newInstrument(name, export.CounterKind, numberKind, opts) | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-11 20:21:34 -07:00
										 |  |  | func (m *SDK) newMeasureInstrument(name string, numberKind core.NumberKind, opts []api.Option) *instrument { | 
					
						
							|  |  |  | 	return m.newInstrument(name, export.MeasureKind, numberKind, opts) | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-11 20:21:34 -07:00
										 |  |  | func (m *SDK) NewInt64Counter(name string, opts ...api.Option) (api.Int64Counter, error) { | 
					
						
							|  |  |  | 	return api.WrapInt64CounterInstrument(m.newCounterInstrument(name, core.Int64NumberKind, opts), nil) | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-11 20:21:34 -07:00
										 |  |  | func (m *SDK) NewFloat64Counter(name string, opts ...api.Option) (api.Float64Counter, error) { | 
					
						
							|  |  |  | 	return api.WrapFloat64CounterInstrument(m.newCounterInstrument(name, core.Float64NumberKind, opts), nil) | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-11 20:21:34 -07:00
										 |  |  | func (m *SDK) NewInt64Measure(name string, opts ...api.Option) (api.Int64Measure, error) { | 
					
						
							|  |  |  | 	return api.WrapInt64MeasureInstrument(m.newMeasureInstrument(name, core.Int64NumberKind, opts), nil) | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-11 20:21:34 -07:00
										 |  |  | func (m *SDK) NewFloat64Measure(name string, opts ...api.Option) (api.Float64Measure, error) { | 
					
						
							|  |  |  | 	return api.WrapFloat64MeasureInstrument(m.newMeasureInstrument(name, core.Float64NumberKind, opts), nil) | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-11 20:21:34 -07:00
										 |  |  | func (m *SDK) RegisterInt64Observer(name string, callback api.Int64ObserverCallback, opts ...api.Option) (api.Int64Observer, error) { | 
					
						
							| 
									
										
										
										
											2020-03-05 12:15:30 -08:00
										 |  |  | 	if callback == nil { | 
					
						
							|  |  |  | 		return api.NoopMeter{}.RegisterInt64Observer("", nil) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-03-11 20:21:34 -07:00
										 |  |  | 	descriptor := newDescriptor(name, export.ObserverKind, core.Int64NumberKind, opts) | 
					
						
							| 
									
										
										
										
											2020-03-05 12:15:30 -08:00
										 |  |  | 	cb := wrapInt64ObserverCallback(callback) | 
					
						
							|  |  |  | 	obs := m.newObserver(descriptor, cb) | 
					
						
							|  |  |  | 	return int64Observer{ | 
					
						
							|  |  |  | 		observer: obs, | 
					
						
							| 
									
										
										
										
											2020-03-11 11:57:57 -07:00
										 |  |  | 	}, nil | 
					
						
							| 
									
										
										
										
											2020-03-05 12:15:30 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func wrapInt64ObserverCallback(callback api.Int64ObserverCallback) observerCallback { | 
					
						
							|  |  |  | 	return func(result observerResult) { | 
					
						
							|  |  |  | 		typeSafeResult := int64ObserverResult{ | 
					
						
							|  |  |  | 			result: result, | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		callback(typeSafeResult) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-11 20:21:34 -07:00
										 |  |  | func (m *SDK) RegisterFloat64Observer(name string, callback api.Float64ObserverCallback, opts ...api.Option) (api.Float64Observer, error) { | 
					
						
							| 
									
										
										
										
											2020-03-05 12:15:30 -08:00
										 |  |  | 	if callback == nil { | 
					
						
							|  |  |  | 		return api.NoopMeter{}.RegisterFloat64Observer("", nil) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-03-11 20:21:34 -07:00
										 |  |  | 	descriptor := newDescriptor(name, export.ObserverKind, core.Float64NumberKind, opts) | 
					
						
							| 
									
										
										
										
											2020-03-05 12:15:30 -08:00
										 |  |  | 	cb := wrapFloat64ObserverCallback(callback) | 
					
						
							|  |  |  | 	obs := m.newObserver(descriptor, cb) | 
					
						
							|  |  |  | 	return float64Observer{ | 
					
						
							|  |  |  | 		observer: obs, | 
					
						
							| 
									
										
										
										
											2020-03-11 11:57:57 -07:00
										 |  |  | 	}, nil | 
					
						
							| 
									
										
										
										
											2020-03-05 12:15:30 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func wrapFloat64ObserverCallback(callback api.Float64ObserverCallback) observerCallback { | 
					
						
							|  |  |  | 	return func(result observerResult) { | 
					
						
							|  |  |  | 		typeSafeResult := float64ObserverResult{ | 
					
						
							|  |  |  | 			result: result, | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		callback(typeSafeResult) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (m *SDK) newObserver(descriptor *export.Descriptor, callback observerCallback) *observer { | 
					
						
							|  |  |  | 	obs := &observer{ | 
					
						
							|  |  |  | 		meter:      m, | 
					
						
							|  |  |  | 		descriptor: descriptor, | 
					
						
							|  |  |  | 		recorders:  nil, | 
					
						
							|  |  |  | 		callback:   callback, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	m.observers.Store(obs, nil) | 
					
						
							|  |  |  | 	return obs | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Collect traverses the list of active records and observers and | 
					
						
							|  |  |  | // exports data for each active instrument.  Collect() may not be | 
					
						
							|  |  |  | // called concurrently. | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | // | 
					
						
							| 
									
										
										
										
											2019-11-05 13:08:55 -08:00
										 |  |  | // During the collection pass, the export.Batcher will receive | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | // one Export() call per current aggregation. | 
					
						
							| 
									
										
										
										
											2019-11-15 13:01:20 -08:00
										 |  |  | // | 
					
						
							|  |  |  | // Returns the number of records that were checkpointed. | 
					
						
							|  |  |  | func (m *SDK) Collect(ctx context.Context) int { | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | 	m.collectLock.Lock() | 
					
						
							|  |  |  | 	defer m.collectLock.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-05 12:15:30 -08:00
										 |  |  | 	checkpointed := m.collectRecords(ctx) | 
					
						
							|  |  |  | 	checkpointed += m.collectObservers(ctx) | 
					
						
							|  |  |  | 	m.currentEpoch++ | 
					
						
							|  |  |  | 	return checkpointed | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (m *SDK) collectRecords(ctx context.Context) int { | 
					
						
							| 
									
										
										
										
											2019-11-15 13:01:20 -08:00
										 |  |  | 	checkpointed := 0 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-06 14:45:56 -08:00
										 |  |  | 	m.current.Range(func(key interface{}, value interface{}) bool { | 
					
						
							|  |  |  | 		inuse := value.(*record) | 
					
						
							|  |  |  | 		unmapped := inuse.refMapped.tryUnmap() | 
					
						
							|  |  |  | 		// If able to unmap then remove the record from the current Map. | 
					
						
							|  |  |  | 		if unmapped { | 
					
						
							|  |  |  | 			m.current.Delete(inuse.mapkey()) | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-06 14:45:56 -08:00
										 |  |  | 		// Always report the values if a reference to the Record is active, | 
					
						
							|  |  |  | 		// this is to keep the previous behavior. | 
					
						
							|  |  |  | 		// TODO: Reconsider this logic. | 
					
						
							|  |  |  | 		if inuse.refMapped.inUse() || atomic.LoadInt64(&inuse.modified) != 0 { | 
					
						
							|  |  |  | 			atomic.StoreInt64(&inuse.modified, 0) | 
					
						
							| 
									
										
										
										
											2020-03-05 12:15:30 -08:00
										 |  |  | 			checkpointed += m.checkpointRecord(ctx, inuse) | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-06 14:45:56 -08:00
										 |  |  | 		// Always continue to iterate over the entire map. | 
					
						
							|  |  |  | 		return true | 
					
						
							|  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-15 13:01:20 -08:00
										 |  |  | 	return checkpointed | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-05 12:15:30 -08:00
										 |  |  | func (m *SDK) collectObservers(ctx context.Context) int { | 
					
						
							|  |  |  | 	checkpointed := 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	m.observers.Range(func(key, value interface{}) bool { | 
					
						
							|  |  |  | 		obs := key.(*observer) | 
					
						
							|  |  |  | 		result := observerResult{ | 
					
						
							|  |  |  | 			observer: obs, | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		obs.callback(result) | 
					
						
							|  |  |  | 		checkpointed += m.checkpointObserver(ctx, obs) | 
					
						
							|  |  |  | 		return true | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return checkpointed | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (m *SDK) checkpointRecord(ctx context.Context, r *record) int { | 
					
						
							|  |  |  | 	return m.checkpoint(ctx, r.descriptor, r.recorder, r.labels) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (m *SDK) checkpointObserver(ctx context.Context, obs *observer) int { | 
					
						
							|  |  |  | 	if len(obs.recorders) == 0 { | 
					
						
							| 
									
										
										
										
											2019-11-15 13:01:20 -08:00
										 |  |  | 		return 0 | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-03-05 12:15:30 -08:00
										 |  |  | 	checkpointed := 0 | 
					
						
							|  |  |  | 	for encodedLabels, lrec := range obs.recorders { | 
					
						
							|  |  |  | 		epochDiff := m.currentEpoch - lrec.modifiedEpoch | 
					
						
							|  |  |  | 		if epochDiff == 0 { | 
					
						
							|  |  |  | 			checkpointed += m.checkpoint(ctx, obs.descriptor, lrec.recorder, lrec.labels) | 
					
						
							|  |  |  | 		} else if epochDiff > 1 { | 
					
						
							|  |  |  | 			// This is second collection cycle with no | 
					
						
							|  |  |  | 			// observations for this labelset. Remove the | 
					
						
							|  |  |  | 			// recorder. | 
					
						
							|  |  |  | 			delete(obs.recorders, encodedLabels) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if len(obs.recorders) == 0 { | 
					
						
							|  |  |  | 		obs.recorders = nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return checkpointed | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-11-15 13:01:20 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-05 12:15:30 -08:00
										 |  |  | func (m *SDK) checkpoint(ctx context.Context, descriptor *export.Descriptor, recorder export.Aggregator, labels *labels) int { | 
					
						
							|  |  |  | 	if recorder == nil { | 
					
						
							|  |  |  | 		return 0 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	recorder.Checkpoint(ctx, descriptor) | 
					
						
							| 
									
										
										
										
											2020-03-11 09:11:27 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// TODO Labels are encoded once per collection interval, | 
					
						
							|  |  |  | 	// instead of once per bound instrument lifetime.  This can be | 
					
						
							|  |  |  | 	// addressed similarly to OTEP 78, see | 
					
						
							|  |  |  | 	// https://github.com/jmacd/opentelemetry-go/blob/8bed2e14df7f9f4688fbab141924bb786dc9a3a1/api/context/internal/set.go#L89 | 
					
						
							|  |  |  | 	exportLabels := export.NewLabels(labels.slice, m.labelEncoder.Encode(labels.slice), m.labelEncoder) | 
					
						
							| 
									
										
										
										
											2020-03-05 12:15:30 -08:00
										 |  |  | 	exportRecord := export.NewRecord(descriptor, exportLabels, recorder) | 
					
						
							|  |  |  | 	err := m.batcher.Process(ctx, exportRecord) | 
					
						
							| 
									
										
										
										
											2019-11-15 13:01:20 -08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		m.errorHandler(err) | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-11-15 13:01:20 -08:00
										 |  |  | 	return 1 | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // RecordBatch enters a batch of metric events. | 
					
						
							|  |  |  | func (m *SDK) RecordBatch(ctx context.Context, ls api.LabelSet, measurements ...api.Measurement) { | 
					
						
							|  |  |  | 	for _, meas := range measurements { | 
					
						
							|  |  |  | 		meas.InstrumentImpl().RecordOne(ctx, meas.Number(), ls) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // GetDescriptor returns the descriptor of an instrument, which is not | 
					
						
							|  |  |  | // part of the public metric API. | 
					
						
							|  |  |  | func (m *SDK) GetDescriptor(inst metric.InstrumentImpl) *export.Descriptor { | 
					
						
							|  |  |  | 	if ii, ok := inst.(*instrument); ok { | 
					
						
							|  |  |  | 		return ii.descriptor | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (r *record) RecordOne(ctx context.Context, number core.Number) { | 
					
						
							| 
									
										
										
										
											2019-11-15 13:01:20 -08:00
										 |  |  | 	if r.recorder == nil { | 
					
						
							|  |  |  | 		// The instrument is disabled according to the AggregationSelector. | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if err := aggregator.RangeTest(number, r.descriptor); err != nil { | 
					
						
							|  |  |  | 		r.labels.meter.errorHandler(err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if err := r.recorder.Update(ctx, number, r.descriptor); err != nil { | 
					
						
							|  |  |  | 		r.labels.meter.errorHandler(err) | 
					
						
							|  |  |  | 		return | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-27 16:30:19 -08:00
										 |  |  | func (r *record) Unbind() { | 
					
						
							| 
									
										
										
										
											2020-02-06 14:45:56 -08:00
										 |  |  | 	// Record was modified, inform the Collect() that things need to be collected. | 
					
						
							|  |  |  | 	// TODO: Reconsider if we should marked as modified when an Update happens and | 
					
						
							|  |  |  | 	// collect only when updates happened even for Bounds. | 
					
						
							|  |  |  | 	atomic.StoreInt64(&r.modified, 1) | 
					
						
							|  |  |  | 	r.refMapped.unref() | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (r *record) mapkey() mapkey { | 
					
						
							|  |  |  | 	return mapkey{ | 
					
						
							|  |  |  | 		descriptor: r.descriptor, | 
					
						
							| 
									
										
										
										
											2020-03-11 09:11:27 -07:00
										 |  |  | 		ordered:    r.labels.ordered, | 
					
						
							| 
									
										
										
										
											2019-10-29 13:27:22 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | } |