You've already forked opentelemetry-go
							
							
				mirror of
				https://github.com/open-telemetry/opentelemetry-go.git
				synced 2025-10-31 00:07:40 +02:00 
			
		
		
		
	Global meter forwarding implementation (#392)
* Initial skeleton * Revert noop provider removal * Checkpoint * Checkpoint * Implement Bound instrument and LabelSet * Add test * Add a benchmark * Add a release test * Document LabelSetDelegator * Lint and comments * Add a second Meter test; fix typo; add a panic * Add a test for the builtin SDK * Address feedback
This commit is contained in:
		| @@ -15,55 +15,36 @@ | ||||
| package global | ||||
|  | ||||
| import ( | ||||
| 	"sync/atomic" | ||||
|  | ||||
| 	"go.opentelemetry.io/otel/api/global/internal" | ||||
| 	"go.opentelemetry.io/otel/api/metric" | ||||
| 	"go.opentelemetry.io/otel/api/trace" | ||||
| ) | ||||
|  | ||||
| type ( | ||||
| 	traceProvider struct { | ||||
| 		tp trace.Provider | ||||
| 	} | ||||
|  | ||||
| 	meterProvider struct { | ||||
| 		mp metric.Provider | ||||
| 	} | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	globalTracer atomic.Value | ||||
| 	globalMeter  atomic.Value | ||||
| ) | ||||
|  | ||||
| // TraceProvider returns the registered global trace provider. | ||||
| // If none is registered then an instance of trace.NoopProvider is returned. | ||||
| // | ||||
| // Use the trace provider to create a named tracer. E.g. | ||||
| //     tracer := global.TraceProvider().Tracer("example.com/foo") | ||||
| func TraceProvider() trace.Provider { | ||||
| 	if gp := globalTracer.Load(); gp != nil { | ||||
| 		return gp.(traceProvider).tp | ||||
| 	} | ||||
| 	return trace.NoopProvider{} | ||||
| 	return internal.TraceProvider() | ||||
| } | ||||
|  | ||||
| // SetTraceProvider registers `tp` as the global trace provider. | ||||
| func SetTraceProvider(tp trace.Provider) { | ||||
| 	globalTracer.Store(traceProvider{tp: tp}) | ||||
| 	internal.SetTraceProvider(tp) | ||||
| } | ||||
|  | ||||
| // MeterProvider returns the registered global meter provider. | ||||
| // If none is registered then an instance of metric.NoopProvider is returned. | ||||
| // Use the trace provider to create a named meter. E.g. | ||||
| // MeterProvider returns the registered global meter provider.  If | ||||
| // none is registered then a default meter provider is returned that | ||||
| // forwards the Meter interface to the first registered Meter. | ||||
| // | ||||
| // Use the meter provider to create a named meter. E.g. | ||||
| //     meter := global.MeterProvider().Meter("example.com/foo") | ||||
| func MeterProvider() metric.Provider { | ||||
| 	if gp := globalMeter.Load(); gp != nil { | ||||
| 		return gp.(meterProvider).mp | ||||
| 	} | ||||
| 	return metric.NoopProvider{} | ||||
| 	return internal.MeterProvider() | ||||
| } | ||||
|  | ||||
| // SetMeterProvider registers `mp` as the global meter provider. | ||||
| func SetMeterProvider(mp metric.Provider) { | ||||
| 	globalMeter.Store(meterProvider{mp: mp}) | ||||
| 	internal.SetMeterProvider(mp) | ||||
| } | ||||
|   | ||||
							
								
								
									
										102
									
								
								api/global/internal/benchmark_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								api/global/internal/benchmark_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,102 @@ | ||||
| package internal_test | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"strings" | ||||
| 	"testing" | ||||
|  | ||||
| 	"go.opentelemetry.io/otel/api/global" | ||||
| 	"go.opentelemetry.io/otel/api/global/internal" | ||||
| 	"go.opentelemetry.io/otel/api/key" | ||||
| 	"go.opentelemetry.io/otel/api/metric" | ||||
| 	export "go.opentelemetry.io/otel/sdk/export/metric" | ||||
| 	sdk "go.opentelemetry.io/otel/sdk/metric" | ||||
| 	"go.opentelemetry.io/otel/sdk/metric/aggregator/counter" | ||||
| 	"go.opentelemetry.io/otel/sdk/metric/aggregator/ddsketch" | ||||
| 	"go.opentelemetry.io/otel/sdk/metric/aggregator/gauge" | ||||
| 	"go.opentelemetry.io/otel/sdk/metric/aggregator/minmaxsumcount" | ||||
| ) | ||||
|  | ||||
| // benchFixture is copied from sdk/metric/benchmark_test.go. | ||||
| // TODO refactor to share this code. | ||||
| type benchFixture struct { | ||||
| 	sdk *sdk.SDK | ||||
| 	B   *testing.B | ||||
| } | ||||
|  | ||||
| var _ metric.Provider = &benchFixture{} | ||||
|  | ||||
| func newFixture(b *testing.B) *benchFixture { | ||||
| 	b.ReportAllocs() | ||||
| 	bf := &benchFixture{ | ||||
| 		B: b, | ||||
| 	} | ||||
| 	bf.sdk = sdk.New(bf, sdk.NewDefaultLabelEncoder()) | ||||
| 	return bf | ||||
| } | ||||
|  | ||||
| func (*benchFixture) AggregatorFor(descriptor *export.Descriptor) export.Aggregator { | ||||
| 	switch descriptor.MetricKind() { | ||||
| 	case export.CounterKind: | ||||
| 		return counter.New() | ||||
| 	case export.GaugeKind: | ||||
| 		return gauge.New() | ||||
| 	case export.MeasureKind: | ||||
| 		if strings.HasSuffix(descriptor.Name(), "minmaxsumcount") { | ||||
| 			return minmaxsumcount.New(descriptor) | ||||
| 		} else if strings.HasSuffix(descriptor.Name(), "ddsketch") { | ||||
| 			return ddsketch.New(ddsketch.NewDefaultConfig(), descriptor) | ||||
| 		} else if strings.HasSuffix(descriptor.Name(), "array") { | ||||
| 			return ddsketch.New(ddsketch.NewDefaultConfig(), descriptor) | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (*benchFixture) Process(context.Context, export.Record) error { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (*benchFixture) CheckpointSet() export.CheckpointSet { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (*benchFixture) FinishedCollection() { | ||||
| } | ||||
|  | ||||
| func (fix *benchFixture) Meter(name string) metric.Meter { | ||||
| 	return fix.sdk | ||||
| } | ||||
|  | ||||
| func BenchmarkGlobalInt64CounterAddNoSDK(b *testing.B) { | ||||
| 	internal.ResetForTest() | ||||
| 	ctx := context.Background() | ||||
| 	sdk := global.MeterProvider().Meter("test") | ||||
| 	labs := sdk.Labels(key.String("A", "B")) | ||||
| 	cnt := sdk.NewInt64Counter("int64.counter") | ||||
|  | ||||
| 	b.ResetTimer() | ||||
|  | ||||
| 	for i := 0; i < b.N; i++ { | ||||
| 		cnt.Add(ctx, 1, labs) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func BenchmarkGlobalInt64CounterAddWithSDK(b *testing.B) { | ||||
| 	// Comapare with BenchmarkInt64CounterAdd() in ../../sdk/meter/benchmark_test.go | ||||
| 	ctx := context.Background() | ||||
| 	fix := newFixture(b) | ||||
|  | ||||
| 	sdk := global.MeterProvider().Meter("test") | ||||
|  | ||||
| 	global.SetMeterProvider(fix) | ||||
|  | ||||
| 	labs := sdk.Labels(key.String("A", "B")) | ||||
| 	cnt := sdk.NewInt64Counter("int64.counter") | ||||
|  | ||||
| 	b.ResetTimer() | ||||
|  | ||||
| 	for i := 0; i < b.N; i++ { | ||||
| 		cnt.Add(ctx, 1, labs) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										293
									
								
								api/global/internal/meter.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										293
									
								
								api/global/internal/meter.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,293 @@ | ||||
| package internal | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"sync" | ||||
| 	"sync/atomic" | ||||
| 	"unsafe" | ||||
|  | ||||
| 	"go.opentelemetry.io/otel/api/core" | ||||
| 	"go.opentelemetry.io/otel/api/metric" | ||||
| ) | ||||
|  | ||||
| // This file contains the forwarding implementation of metric.Provider | ||||
| // used as the default global instance.  Metric events using instruments | ||||
| // provided by this implementation are no-ops until the first Meter | ||||
| // implementation is set as the global provider. | ||||
| // | ||||
| // The implementation here uses Mutexes to maintain a list of active | ||||
| // Meters in the Provider and Instruments in each Meter, under the | ||||
| // assumption that these interfaces are not performance-critical. | ||||
| // | ||||
| // We have the invariant that setDelegate() will be called before a | ||||
| // new metric.Provider implementation is registered as the global | ||||
| // provider.  Mutexes in the Provider and Meters ensure that each | ||||
| // instrument has a delegate before the global provider is set. | ||||
| // | ||||
| // LabelSets are implemented by delegating to the Meter instance using | ||||
| // the metric.LabelSetDelegator interface. | ||||
| // | ||||
| // Bound instrument operations are implemented by delegating to the | ||||
| // instrument after it is registered, with a sync.Once initializer to | ||||
| // protect against races with Release(). | ||||
|  | ||||
| type metricKind int8 | ||||
|  | ||||
| const ( | ||||
| 	counterKind metricKind = iota | ||||
| 	gaugeKind | ||||
| 	measureKind | ||||
| ) | ||||
|  | ||||
| type meterProvider struct { | ||||
| 	lock     sync.Mutex | ||||
| 	meters   []*meter | ||||
| 	delegate metric.Provider | ||||
| } | ||||
|  | ||||
| type meter struct { | ||||
| 	provider *meterProvider | ||||
| 	name     string | ||||
|  | ||||
| 	lock        sync.Mutex | ||||
| 	instruments []*instImpl | ||||
|  | ||||
| 	delegate unsafe.Pointer // (*metric.Meter) | ||||
| } | ||||
|  | ||||
| type instImpl struct { | ||||
| 	name  string | ||||
| 	mkind metricKind | ||||
| 	nkind core.NumberKind | ||||
| 	opts  interface{} | ||||
|  | ||||
| 	delegate unsafe.Pointer // (*metric.InstrumentImpl) | ||||
| } | ||||
|  | ||||
| type labelSet struct { | ||||
| 	meter *meter | ||||
| 	value []core.KeyValue | ||||
|  | ||||
| 	initialize sync.Once | ||||
| 	delegate   unsafe.Pointer // (* metric.LabelSet) | ||||
| } | ||||
|  | ||||
| type instHandle struct { | ||||
| 	inst   *instImpl | ||||
| 	labels metric.LabelSet | ||||
|  | ||||
| 	initialize sync.Once | ||||
| 	delegate   unsafe.Pointer // (*metric.HandleImpl) | ||||
| } | ||||
|  | ||||
| var _ metric.Provider = &meterProvider{} | ||||
| var _ metric.Meter = &meter{} | ||||
| var _ metric.LabelSet = &labelSet{} | ||||
| var _ metric.LabelSetDelegate = &labelSet{} | ||||
| var _ metric.InstrumentImpl = &instImpl{} | ||||
| var _ metric.HandleImpl = &instHandle{} | ||||
|  | ||||
| // Provider interface and delegation | ||||
|  | ||||
| func (p *meterProvider) setDelegate(provider metric.Provider) { | ||||
| 	p.lock.Lock() | ||||
| 	defer p.lock.Unlock() | ||||
|  | ||||
| 	p.delegate = provider | ||||
| 	for _, m := range p.meters { | ||||
| 		m.setDelegate(provider) | ||||
| 	} | ||||
| 	p.meters = nil | ||||
| } | ||||
|  | ||||
| func (p *meterProvider) Meter(name string) metric.Meter { | ||||
| 	p.lock.Lock() | ||||
| 	defer p.lock.Unlock() | ||||
|  | ||||
| 	if p.delegate != nil { | ||||
| 		return p.delegate.Meter(name) | ||||
| 	} | ||||
|  | ||||
| 	m := &meter{ | ||||
| 		provider: p, | ||||
| 		name:     name, | ||||
| 	} | ||||
| 	p.meters = append(p.meters, m) | ||||
| 	return m | ||||
| } | ||||
|  | ||||
| // Meter interface and delegation | ||||
|  | ||||
| func (m *meter) setDelegate(provider metric.Provider) { | ||||
| 	m.lock.Lock() | ||||
| 	defer m.lock.Unlock() | ||||
|  | ||||
| 	d := new(metric.Meter) | ||||
| 	*d = provider.Meter(m.name) | ||||
| 	m.delegate = unsafe.Pointer(d) | ||||
|  | ||||
| 	for _, inst := range m.instruments { | ||||
| 		inst.setDelegate(*d) | ||||
| 	} | ||||
| 	m.instruments = nil | ||||
| } | ||||
|  | ||||
| func (m *meter) newInst(name string, mkind metricKind, nkind core.NumberKind, opts interface{}) metric.InstrumentImpl { | ||||
| 	m.lock.Lock() | ||||
| 	defer m.lock.Unlock() | ||||
|  | ||||
| 	if meterPtr := (*metric.Meter)(atomic.LoadPointer(&m.delegate)); meterPtr != nil { | ||||
| 		return newInstDelegate(*meterPtr, name, mkind, nkind, opts) | ||||
| 	} | ||||
|  | ||||
| 	inst := &instImpl{ | ||||
| 		name:  name, | ||||
| 		mkind: mkind, | ||||
| 		nkind: nkind, | ||||
| 		opts:  opts, | ||||
| 	} | ||||
| 	m.instruments = append(m.instruments, inst) | ||||
| 	return inst | ||||
| } | ||||
|  | ||||
| func newInstDelegate(m metric.Meter, name string, mkind metricKind, nkind core.NumberKind, opts interface{}) metric.InstrumentImpl { | ||||
| 	switch mkind { | ||||
| 	case counterKind: | ||||
| 		if nkind == core.Int64NumberKind { | ||||
| 			return m.NewInt64Counter(name, opts.([]metric.CounterOptionApplier)...).Impl() | ||||
| 		} | ||||
| 		return m.NewFloat64Counter(name, opts.([]metric.CounterOptionApplier)...).Impl() | ||||
| 	case gaugeKind: | ||||
| 		if nkind == core.Int64NumberKind { | ||||
| 			return m.NewInt64Gauge(name, opts.([]metric.GaugeOptionApplier)...).Impl() | ||||
| 		} | ||||
| 		return m.NewFloat64Gauge(name, opts.([]metric.GaugeOptionApplier)...).Impl() | ||||
| 	case measureKind: | ||||
| 		if nkind == core.Int64NumberKind { | ||||
| 			return m.NewInt64Measure(name, opts.([]metric.MeasureOptionApplier)...).Impl() | ||||
| 		} | ||||
| 		return m.NewFloat64Measure(name, opts.([]metric.MeasureOptionApplier)...).Impl() | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Instrument delegation | ||||
|  | ||||
| func (inst *instImpl) setDelegate(d metric.Meter) { | ||||
| 	implPtr := new(metric.InstrumentImpl) | ||||
|  | ||||
| 	*implPtr = newInstDelegate(d, inst.name, inst.mkind, inst.nkind, inst.opts) | ||||
|  | ||||
| 	atomic.StorePointer(&inst.delegate, unsafe.Pointer(implPtr)) | ||||
| } | ||||
|  | ||||
| func (inst *instImpl) AcquireHandle(labels metric.LabelSet) metric.HandleImpl { | ||||
| 	if implPtr := (*metric.InstrumentImpl)(atomic.LoadPointer(&inst.delegate)); implPtr != nil { | ||||
| 		return (*implPtr).AcquireHandle(labels) | ||||
| 	} | ||||
| 	return &instHandle{ | ||||
| 		inst:   inst, | ||||
| 		labels: labels, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (bound *instHandle) Release() { | ||||
| 	bound.initialize.Do(func() {}) | ||||
|  | ||||
| 	implPtr := (*metric.HandleImpl)(atomic.LoadPointer(&bound.delegate)) | ||||
|  | ||||
| 	if implPtr == nil { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	(*implPtr).Release() | ||||
| } | ||||
|  | ||||
| // Metric updates | ||||
|  | ||||
| func (m *meter) RecordBatch(ctx context.Context, labels metric.LabelSet, measurements ...metric.Measurement) { | ||||
| 	if delegatePtr := (*metric.Meter)(atomic.LoadPointer(&m.delegate)); delegatePtr != nil { | ||||
| 		(*delegatePtr).RecordBatch(ctx, labels, measurements...) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (inst *instImpl) RecordOne(ctx context.Context, number core.Number, labels metric.LabelSet) { | ||||
| 	if instPtr := (*metric.InstrumentImpl)(atomic.LoadPointer(&inst.delegate)); instPtr != nil { | ||||
| 		(*instPtr).RecordOne(ctx, number, labels) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Bound instrument initialization | ||||
|  | ||||
| func (bound *instHandle) RecordOne(ctx context.Context, number core.Number) { | ||||
| 	instPtr := (*metric.InstrumentImpl)(atomic.LoadPointer(&bound.inst.delegate)) | ||||
| 	if instPtr == nil { | ||||
| 		return | ||||
| 	} | ||||
| 	var implPtr *metric.HandleImpl | ||||
| 	bound.initialize.Do(func() { | ||||
|  | ||||
| 		implPtr = new(metric.HandleImpl) | ||||
| 		*implPtr = (*instPtr).AcquireHandle(bound.labels) | ||||
| 		atomic.StorePointer(&bound.delegate, unsafe.Pointer(implPtr)) | ||||
| 	}) | ||||
| 	if implPtr == nil { | ||||
| 		implPtr = (*metric.HandleImpl)(atomic.LoadPointer(&bound.delegate)) | ||||
| 	} | ||||
| 	(*implPtr).RecordOne(ctx, number) | ||||
| } | ||||
|  | ||||
| // LabelSet initialization | ||||
|  | ||||
| func (m *meter) Labels(labels ...core.KeyValue) metric.LabelSet { | ||||
| 	return &labelSet{ | ||||
| 		meter: m, | ||||
| 		value: labels, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (labels *labelSet) Delegate() metric.LabelSet { | ||||
| 	meterPtr := (*metric.Meter)(atomic.LoadPointer(&labels.meter.delegate)) | ||||
| 	if meterPtr == nil { | ||||
| 		// This is technically impossible, provided the global | ||||
| 		// Meter is updated after the meters and instruments | ||||
| 		// have been delegated. | ||||
| 		return labels | ||||
| 	} | ||||
| 	var implPtr *metric.LabelSet | ||||
| 	labels.initialize.Do(func() { | ||||
| 		implPtr = new(metric.LabelSet) | ||||
| 		*implPtr = (*meterPtr).Labels(labels.value...) | ||||
| 		atomic.StorePointer(&labels.delegate, unsafe.Pointer(implPtr)) | ||||
| 	}) | ||||
| 	if implPtr == nil { | ||||
| 		implPtr = (*metric.LabelSet)(atomic.LoadPointer(&labels.delegate)) | ||||
| 	} | ||||
| 	return (*implPtr) | ||||
| } | ||||
|  | ||||
| // Constructors | ||||
|  | ||||
| func (m *meter) NewInt64Counter(name string, opts ...metric.CounterOptionApplier) metric.Int64Counter { | ||||
| 	return metric.WrapInt64CounterInstrument(m.newInst(name, counterKind, core.Int64NumberKind, opts)) | ||||
| } | ||||
|  | ||||
| func (m *meter) NewFloat64Counter(name string, opts ...metric.CounterOptionApplier) metric.Float64Counter { | ||||
| 	return metric.WrapFloat64CounterInstrument(m.newInst(name, counterKind, core.Float64NumberKind, opts)) | ||||
| } | ||||
|  | ||||
| func (m *meter) NewInt64Gauge(name string, opts ...metric.GaugeOptionApplier) metric.Int64Gauge { | ||||
| 	return metric.WrapInt64GaugeInstrument(m.newInst(name, gaugeKind, core.Int64NumberKind, opts)) | ||||
| } | ||||
|  | ||||
| func (m *meter) NewFloat64Gauge(name string, opts ...metric.GaugeOptionApplier) metric.Float64Gauge { | ||||
| 	return metric.WrapFloat64GaugeInstrument(m.newInst(name, gaugeKind, core.Float64NumberKind, opts)) | ||||
| } | ||||
|  | ||||
| func (m *meter) NewInt64Measure(name string, opts ...metric.MeasureOptionApplier) metric.Int64Measure { | ||||
| 	return metric.WrapInt64MeasureInstrument(m.newInst(name, measureKind, core.Int64NumberKind, opts)) | ||||
| } | ||||
|  | ||||
| func (m *meter) NewFloat64Measure(name string, opts ...metric.MeasureOptionApplier) metric.Float64Measure { | ||||
| 	return metric.WrapFloat64MeasureInstrument(m.newInst(name, measureKind, core.Float64NumberKind, opts)) | ||||
| } | ||||
							
								
								
									
										244
									
								
								api/global/internal/meter_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										244
									
								
								api/global/internal/meter_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,244 @@ | ||||
| package internal_test | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"io" | ||||
| 	"io/ioutil" | ||||
| 	"testing" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/stretchr/testify/require" | ||||
|  | ||||
| 	"go.opentelemetry.io/otel/api/core" | ||||
| 	"go.opentelemetry.io/otel/api/global" | ||||
| 	"go.opentelemetry.io/otel/api/global/internal" | ||||
| 	"go.opentelemetry.io/otel/api/key" | ||||
| 	"go.opentelemetry.io/otel/exporter/metric/stdout" | ||||
| 	metrictest "go.opentelemetry.io/otel/internal/metric" | ||||
| 	"go.opentelemetry.io/otel/sdk/metric/batcher/ungrouped" | ||||
| 	"go.opentelemetry.io/otel/sdk/metric/controller/push" | ||||
| 	"go.opentelemetry.io/otel/sdk/metric/selector/simple" | ||||
| ) | ||||
|  | ||||
| func TestDirect(t *testing.T) { | ||||
| 	internal.ResetForTest() | ||||
|  | ||||
| 	ctx := context.Background() | ||||
| 	meter1 := global.MeterProvider().Meter("test1") | ||||
| 	meter2 := global.MeterProvider().Meter("test2") | ||||
| 	lvals1 := key.String("A", "B") | ||||
| 	labels1 := meter1.Labels(lvals1) | ||||
| 	lvals2 := key.String("C", "D") | ||||
| 	labels2 := meter1.Labels(lvals2) | ||||
| 	lvals3 := key.String("E", "F") | ||||
| 	labels3 := meter2.Labels(lvals3) | ||||
|  | ||||
| 	counter := meter1.NewInt64Counter("test.counter") | ||||
| 	counter.Add(ctx, 1, labels1) | ||||
| 	counter.Add(ctx, 1, labels1) | ||||
|  | ||||
| 	gauge := meter1.NewInt64Gauge("test.gauge") | ||||
| 	gauge.Set(ctx, 1, labels2) | ||||
| 	gauge.Set(ctx, 2, labels2) | ||||
|  | ||||
| 	measure := meter1.NewFloat64Measure("test.measure") | ||||
| 	measure.Record(ctx, 1, labels1) | ||||
| 	measure.Record(ctx, 2, labels1) | ||||
|  | ||||
| 	second := meter2.NewFloat64Measure("test.second") | ||||
| 	second.Record(ctx, 1, labels3) | ||||
| 	second.Record(ctx, 2, labels3) | ||||
|  | ||||
| 	sdk := metrictest.NewProvider() | ||||
| 	global.SetMeterProvider(sdk) | ||||
|  | ||||
| 	counter.Add(ctx, 1, labels1) | ||||
| 	gauge.Set(ctx, 3, labels2) | ||||
| 	measure.Record(ctx, 3, labels1) | ||||
| 	second.Record(ctx, 3, labels3) | ||||
|  | ||||
| 	mock := sdk.Meter("test1").(*metrictest.Meter) | ||||
| 	require.Equal(t, 3, len(mock.MeasurementBatches)) | ||||
|  | ||||
| 	require.Equal(t, map[core.Key]core.Value{ | ||||
| 		lvals1.Key: lvals1.Value, | ||||
| 	}, mock.MeasurementBatches[0].LabelSet.Labels) | ||||
| 	require.Equal(t, 1, len(mock.MeasurementBatches[0].Measurements)) | ||||
| 	require.Equal(t, core.NewInt64Number(1), | ||||
| 		mock.MeasurementBatches[0].Measurements[0].Number) | ||||
| 	require.Equal(t, "test.counter", | ||||
| 		mock.MeasurementBatches[0].Measurements[0].Instrument.Name) | ||||
|  | ||||
| 	require.Equal(t, map[core.Key]core.Value{ | ||||
| 		lvals2.Key: lvals2.Value, | ||||
| 	}, mock.MeasurementBatches[1].LabelSet.Labels) | ||||
| 	require.Equal(t, 1, len(mock.MeasurementBatches[1].Measurements)) | ||||
| 	require.Equal(t, core.NewInt64Number(3), | ||||
| 		mock.MeasurementBatches[1].Measurements[0].Number) | ||||
| 	require.Equal(t, "test.gauge", | ||||
| 		mock.MeasurementBatches[1].Measurements[0].Instrument.Name) | ||||
|  | ||||
| 	require.Equal(t, map[core.Key]core.Value{ | ||||
| 		lvals1.Key: lvals1.Value, | ||||
| 	}, mock.MeasurementBatches[2].LabelSet.Labels) | ||||
| 	require.Equal(t, 1, len(mock.MeasurementBatches[2].Measurements)) | ||||
| 	require.Equal(t, core.NewFloat64Number(3), | ||||
| 		mock.MeasurementBatches[2].Measurements[0].Number) | ||||
| 	require.Equal(t, "test.measure", | ||||
| 		mock.MeasurementBatches[2].Measurements[0].Instrument.Name) | ||||
|  | ||||
| 	// This tests the second Meter instance | ||||
| 	mock = sdk.Meter("test2").(*metrictest.Meter) | ||||
| 	require.Equal(t, 1, len(mock.MeasurementBatches)) | ||||
|  | ||||
| 	require.Equal(t, map[core.Key]core.Value{ | ||||
| 		lvals3.Key: lvals3.Value, | ||||
| 	}, mock.MeasurementBatches[0].LabelSet.Labels) | ||||
| 	require.Equal(t, 1, len(mock.MeasurementBatches[0].Measurements)) | ||||
| 	require.Equal(t, core.NewFloat64Number(3), | ||||
| 		mock.MeasurementBatches[0].Measurements[0].Number) | ||||
| 	require.Equal(t, "test.second", | ||||
| 		mock.MeasurementBatches[0].Measurements[0].Instrument.Name) | ||||
| } | ||||
|  | ||||
| func TestBound(t *testing.T) { | ||||
| 	internal.ResetForTest() | ||||
|  | ||||
| 	// Note: this test uses oppsite Float64/Int64 number kinds | ||||
| 	// vs. the above, to cover all the instruments. | ||||
| 	ctx := context.Background() | ||||
| 	glob := global.MeterProvider().Meter("test") | ||||
| 	lvals1 := key.String("A", "B") | ||||
| 	labels1 := glob.Labels(lvals1) | ||||
| 	lvals2 := key.String("C", "D") | ||||
| 	labels2 := glob.Labels(lvals2) | ||||
|  | ||||
| 	counter := glob.NewFloat64Counter("test.counter") | ||||
| 	boundC := counter.AcquireHandle(labels1) | ||||
| 	boundC.Add(ctx, 1) | ||||
| 	boundC.Add(ctx, 1) | ||||
|  | ||||
| 	gauge := glob.NewFloat64Gauge("test.gauge") | ||||
| 	boundG := gauge.AcquireHandle(labels2) | ||||
| 	boundG.Set(ctx, 1) | ||||
| 	boundG.Set(ctx, 2) | ||||
|  | ||||
| 	measure := glob.NewInt64Measure("test.measure") | ||||
| 	boundM := measure.AcquireHandle(labels1) | ||||
| 	boundM.Record(ctx, 1) | ||||
| 	boundM.Record(ctx, 2) | ||||
|  | ||||
| 	sdk := metrictest.NewProvider() | ||||
| 	global.SetMeterProvider(sdk) | ||||
|  | ||||
| 	boundC.Add(ctx, 1) | ||||
| 	boundG.Set(ctx, 3) | ||||
| 	boundM.Record(ctx, 3) | ||||
|  | ||||
| 	mock := sdk.Meter("test").(*metrictest.Meter) | ||||
| 	require.Equal(t, 3, len(mock.MeasurementBatches)) | ||||
|  | ||||
| 	require.Equal(t, map[core.Key]core.Value{ | ||||
| 		lvals1.Key: lvals1.Value, | ||||
| 	}, mock.MeasurementBatches[0].LabelSet.Labels) | ||||
| 	require.Equal(t, 1, len(mock.MeasurementBatches[0].Measurements)) | ||||
| 	require.Equal(t, core.NewFloat64Number(1), | ||||
| 		mock.MeasurementBatches[0].Measurements[0].Number) | ||||
| 	require.Equal(t, "test.counter", | ||||
| 		mock.MeasurementBatches[0].Measurements[0].Instrument.Name) | ||||
|  | ||||
| 	require.Equal(t, map[core.Key]core.Value{ | ||||
| 		lvals2.Key: lvals2.Value, | ||||
| 	}, mock.MeasurementBatches[1].LabelSet.Labels) | ||||
| 	require.Equal(t, 1, len(mock.MeasurementBatches[1].Measurements)) | ||||
| 	require.Equal(t, core.NewFloat64Number(3), | ||||
| 		mock.MeasurementBatches[1].Measurements[0].Number) | ||||
| 	require.Equal(t, "test.gauge", | ||||
| 		mock.MeasurementBatches[1].Measurements[0].Instrument.Name) | ||||
|  | ||||
| 	require.Equal(t, map[core.Key]core.Value{ | ||||
| 		lvals1.Key: lvals1.Value, | ||||
| 	}, mock.MeasurementBatches[2].LabelSet.Labels) | ||||
| 	require.Equal(t, 1, len(mock.MeasurementBatches[2].Measurements)) | ||||
| 	require.Equal(t, core.NewInt64Number(3), | ||||
| 		mock.MeasurementBatches[2].Measurements[0].Number) | ||||
| 	require.Equal(t, "test.measure", | ||||
| 		mock.MeasurementBatches[2].Measurements[0].Instrument.Name) | ||||
|  | ||||
| 	boundC.Release() | ||||
| 	boundG.Release() | ||||
| 	boundM.Release() | ||||
| } | ||||
|  | ||||
| func TestRelease(t *testing.T) { | ||||
| 	// Tests Release with SDK never installed. | ||||
| 	internal.ResetForTest() | ||||
|  | ||||
| 	glob := global.MeterProvider().Meter("test") | ||||
| 	lvals1 := key.New("A").String("B") | ||||
| 	labels1 := glob.Labels(lvals1) | ||||
| 	lvals2 := key.New("C").String("D") | ||||
| 	labels2 := glob.Labels(lvals2) | ||||
|  | ||||
| 	counter := glob.NewFloat64Counter("test.counter") | ||||
| 	boundC := counter.AcquireHandle(labels1) | ||||
|  | ||||
| 	gauge := glob.NewFloat64Gauge("test.gauge") | ||||
| 	boundG := gauge.AcquireHandle(labels2) | ||||
|  | ||||
| 	measure := glob.NewInt64Measure("test.measure") | ||||
| 	boundM := measure.AcquireHandle(labels1) | ||||
|  | ||||
| 	boundC.Release() | ||||
| 	boundG.Release() | ||||
| 	boundM.Release() | ||||
| } | ||||
|  | ||||
| func TestDefaultSDK(t *testing.T) { | ||||
| 	internal.ResetForTest() | ||||
|  | ||||
| 	ctx := context.Background() | ||||
| 	meter1 := global.MeterProvider().Meter("builtin") | ||||
| 	lvals1 := key.String("A", "B") | ||||
| 	labels1 := meter1.Labels(lvals1) | ||||
|  | ||||
| 	counter := meter1.NewInt64Counter("test.builtin") | ||||
| 	counter.Add(ctx, 1, labels1) | ||||
| 	counter.Add(ctx, 1, labels1) | ||||
|  | ||||
| 	in, out := io.Pipe() | ||||
| 	// TODO this should equal a stdout.NewPipeline(), use it. | ||||
| 	// Consider also moving the io.Pipe() and go func() call | ||||
| 	// below into a test helper somewhere. | ||||
| 	sdk := func(options stdout.Options) *push.Controller { | ||||
| 		selector := simple.NewWithInexpensiveMeasure() | ||||
| 		exporter, err := stdout.New(options) | ||||
| 		if err != nil { | ||||
| 			panic(err) | ||||
| 		} | ||||
| 		batcher := ungrouped.New(selector, true) | ||||
| 		pusher := push.New(batcher, exporter, time.Second) | ||||
| 		pusher.Start() | ||||
|  | ||||
| 		return pusher | ||||
| 	}(stdout.Options{ | ||||
| 		File:           out, | ||||
| 		DoNotPrintTime: true, | ||||
| 	}) | ||||
|  | ||||
| 	global.SetMeterProvider(sdk) | ||||
|  | ||||
| 	counter.Add(ctx, 1, labels1) | ||||
|  | ||||
| 	ch := make(chan string) | ||||
| 	go func() { | ||||
| 		data, _ := ioutil.ReadAll(in) | ||||
| 		ch <- string(data) | ||||
| 	}() | ||||
|  | ||||
| 	sdk.Stop() | ||||
| 	out.Close() | ||||
|  | ||||
| 	require.Equal(t, `{"updates":[{"name":"test.builtin{A=B}","sum":1}]} | ||||
| `, <-ch) | ||||
| } | ||||
							
								
								
									
										77
									
								
								api/global/internal/state.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								api/global/internal/state.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,77 @@ | ||||
| package internal | ||||
|  | ||||
| import ( | ||||
| 	"sync" | ||||
| 	"sync/atomic" | ||||
|  | ||||
| 	"go.opentelemetry.io/otel/api/metric" | ||||
| 	"go.opentelemetry.io/otel/api/trace" | ||||
| ) | ||||
|  | ||||
| type ( | ||||
| 	traceProviderHolder struct { | ||||
| 		tp trace.Provider | ||||
| 	} | ||||
|  | ||||
| 	meterProviderHolder struct { | ||||
| 		mp metric.Provider | ||||
| 	} | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	globalTracer = defaultTracerValue() | ||||
| 	globalMeter  = defaultMeterValue() | ||||
|  | ||||
| 	delegateMeterOnce sync.Once | ||||
| ) | ||||
|  | ||||
| // TraceProvider is the internal implementation for global.TraceProvider. | ||||
| func TraceProvider() trace.Provider { | ||||
| 	return globalTracer.Load().(traceProviderHolder).tp | ||||
| } | ||||
|  | ||||
| // SetTraceProvider is the internal implementation for global.SetTraceProvider. | ||||
| func SetTraceProvider(tp trace.Provider) { | ||||
| 	globalTracer.Store(traceProviderHolder{tp: tp}) | ||||
| } | ||||
|  | ||||
| // MeterProvider is the internal implementation for global.MeterProvider. | ||||
| func MeterProvider() metric.Provider { | ||||
| 	return globalMeter.Load().(meterProviderHolder).mp | ||||
| } | ||||
|  | ||||
| // SetMeterProvider is the internal implementation for global.SetMeterProvider. | ||||
| func SetMeterProvider(mp metric.Provider) { | ||||
| 	delegateMeterOnce.Do(func() { | ||||
| 		current := MeterProvider() | ||||
|  | ||||
| 		if current == mp { | ||||
| 			// Setting the provider to the prior default is nonsense, panic. | ||||
| 			// Panic is acceptable because we are likely still early in the | ||||
| 			// process lifetime. | ||||
| 			panic("invalid Provider, the global instance cannot be reinstalled") | ||||
| 		} else if def, ok := current.(*meterProvider); ok { | ||||
| 			def.setDelegate(mp) | ||||
| 		} | ||||
| 	}) | ||||
| 	globalMeter.Store(meterProviderHolder{mp: mp}) | ||||
| } | ||||
|  | ||||
| func defaultTracerValue() *atomic.Value { | ||||
| 	v := &atomic.Value{} | ||||
| 	v.Store(traceProviderHolder{tp: trace.NoopProvider{}}) | ||||
| 	return v | ||||
| } | ||||
|  | ||||
| func defaultMeterValue() *atomic.Value { | ||||
| 	v := &atomic.Value{} | ||||
| 	v.Store(meterProviderHolder{mp: &meterProvider{}}) | ||||
| 	return v | ||||
| } | ||||
|  | ||||
| // ResetForTest restores the initial global state, for testing purposes. | ||||
| func ResetForTest() { | ||||
| 	globalTracer = defaultTracerValue() | ||||
| 	globalMeter = defaultMeterValue() | ||||
| 	delegateMeterOnce = sync.Once{} | ||||
| } | ||||
| @@ -20,6 +20,15 @@ import ( | ||||
| 	"go.opentelemetry.io/otel/api/core" | ||||
| ) | ||||
|  | ||||
| // LabelSetDelegate is a general-purpose delegating implementation of | ||||
| // the LabelSet interface.  This is implemented by the default | ||||
| // Provider returned by api/global.SetMeterProvider(), and should be | ||||
| // tested for by implementations before converting a LabelSet to their | ||||
| // private concrete type. | ||||
| type LabelSetDelegate interface { | ||||
| 	Delegate() LabelSet | ||||
| } | ||||
|  | ||||
| // InstrumentImpl is the implementation-level interface Set/Add/Record | ||||
| // individual metrics without precomputed labels. | ||||
| type InstrumentImpl interface { | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= | ||||
| github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= | ||||
| github.com/DataDog/sketches-go v0.0.0-20190923095040-43f19ad77ff7 h1:qELHH0AWCvf98Yf+CNIJx9vOZOfHFDDzgDRYsnNk/vs= | ||||
| github.com/DataDog/sketches-go v0.0.0-20190923095040-43f19ad77ff7/go.mod h1:Q5DbzQ+3AkgGwymQO7aZFNP7ns2lZKGtvRBzRXfdi60= | ||||
| github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= | ||||
| github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM= | ||||
| @@ -7,6 +8,7 @@ github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrU | ||||
| github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= | ||||
| github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= | ||||
| github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= | ||||
| github.com/benbjohnson/clock v1.0.0 h1:78Jk/r6m4wCi6sndMpty7A//t4dw/RW5fV4ZgDVfX1w= | ||||
| github.com/benbjohnson/clock v1.0.0/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= | ||||
| github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= | ||||
| github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= | ||||
| @@ -83,6 +85,7 @@ github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ | ||||
| github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= | ||||
| github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= | ||||
| github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= | ||||
| github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= | ||||
| github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= | ||||
| github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= | ||||
| github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= | ||||
|   | ||||
| @@ -15,6 +15,7 @@ cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiy | ||||
| dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= | ||||
| github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= | ||||
| github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= | ||||
| github.com/DataDog/sketches-go v0.0.0-20190923095040-43f19ad77ff7 h1:qELHH0AWCvf98Yf+CNIJx9vOZOfHFDDzgDRYsnNk/vs= | ||||
| github.com/DataDog/sketches-go v0.0.0-20190923095040-43f19ad77ff7/go.mod h1:Q5DbzQ+3AkgGwymQO7aZFNP7ns2lZKGtvRBzRXfdi60= | ||||
| github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= | ||||
| github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM= | ||||
| @@ -22,6 +23,7 @@ github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrU | ||||
| github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= | ||||
| github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= | ||||
| github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= | ||||
| github.com/benbjohnson/clock v1.0.0 h1:78Jk/r6m4wCi6sndMpty7A//t4dw/RW5fV4ZgDVfX1w= | ||||
| github.com/benbjohnson/clock v1.0.0/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= | ||||
| github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= | ||||
| github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= | ||||
| @@ -103,6 +105,7 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a | ||||
| github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= | ||||
| github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= | ||||
| github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= | ||||
| github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= | ||||
| github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= | ||||
| github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= | ||||
| github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= | ||||
| github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= | ||||
| github.com/DataDog/sketches-go v0.0.0-20190923095040-43f19ad77ff7 h1:qELHH0AWCvf98Yf+CNIJx9vOZOfHFDDzgDRYsnNk/vs= | ||||
| github.com/DataDog/sketches-go v0.0.0-20190923095040-43f19ad77ff7/go.mod h1:Q5DbzQ+3AkgGwymQO7aZFNP7ns2lZKGtvRBzRXfdi60= | ||||
| github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= | ||||
| github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM= | ||||
| @@ -7,6 +8,7 @@ github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrU | ||||
| github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= | ||||
| github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= | ||||
| github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= | ||||
| github.com/benbjohnson/clock v1.0.0 h1:78Jk/r6m4wCi6sndMpty7A//t4dw/RW5fV4ZgDVfX1w= | ||||
| github.com/benbjohnson/clock v1.0.0/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= | ||||
| github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= | ||||
| github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= | ||||
| @@ -85,6 +87,7 @@ github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ | ||||
| github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= | ||||
| github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= | ||||
| github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= | ||||
| github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= | ||||
| github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= | ||||
| github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= | ||||
| github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= | ||||
|   | ||||
| @@ -2,6 +2,7 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT | ||||
| cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= | ||||
| cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= | ||||
| github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= | ||||
| github.com/DataDog/sketches-go v0.0.0-20190923095040-43f19ad77ff7 h1:qELHH0AWCvf98Yf+CNIJx9vOZOfHFDDzgDRYsnNk/vs= | ||||
| github.com/DataDog/sketches-go v0.0.0-20190923095040-43f19ad77ff7/go.mod h1:Q5DbzQ+3AkgGwymQO7aZFNP7ns2lZKGtvRBzRXfdi60= | ||||
| github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= | ||||
| github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM= | ||||
| @@ -11,6 +12,7 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF | ||||
| github.com/apache/thrift v0.13.0 h1:5hryIiq9gtn+MiLVn0wP37kb/uTeRZgN08WoCsAhIhI= | ||||
| github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= | ||||
| github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= | ||||
| github.com/benbjohnson/clock v1.0.0 h1:78Jk/r6m4wCi6sndMpty7A//t4dw/RW5fV4ZgDVfX1w= | ||||
| github.com/benbjohnson/clock v1.0.0/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= | ||||
| github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= | ||||
| github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= | ||||
| @@ -89,6 +91,7 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a | ||||
| github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= | ||||
| github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= | ||||
| github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= | ||||
| github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= | ||||
| github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= | ||||
| github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= | ||||
| github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= | ||||
| github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= | ||||
| github.com/DataDog/sketches-go v0.0.0-20190923095040-43f19ad77ff7 h1:qELHH0AWCvf98Yf+CNIJx9vOZOfHFDDzgDRYsnNk/vs= | ||||
| github.com/DataDog/sketches-go v0.0.0-20190923095040-43f19ad77ff7/go.mod h1:Q5DbzQ+3AkgGwymQO7aZFNP7ns2lZKGtvRBzRXfdi60= | ||||
| github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= | ||||
| github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM= | ||||
| @@ -7,6 +8,7 @@ github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrU | ||||
| github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= | ||||
| github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= | ||||
| github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= | ||||
| github.com/benbjohnson/clock v1.0.0 h1:78Jk/r6m4wCi6sndMpty7A//t4dw/RW5fV4ZgDVfX1w= | ||||
| github.com/benbjohnson/clock v1.0.0/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= | ||||
| github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= | ||||
| github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= | ||||
| @@ -84,6 +86,7 @@ github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ | ||||
| github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= | ||||
| github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= | ||||
| github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= | ||||
| github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= | ||||
| github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= | ||||
| github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= | ||||
| github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= | ||||
|   | ||||
| @@ -2,6 +2,7 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT | ||||
| cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= | ||||
| cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= | ||||
| github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= | ||||
| github.com/DataDog/sketches-go v0.0.0-20190923095040-43f19ad77ff7 h1:qELHH0AWCvf98Yf+CNIJx9vOZOfHFDDzgDRYsnNk/vs= | ||||
| github.com/DataDog/sketches-go v0.0.0-20190923095040-43f19ad77ff7/go.mod h1:Q5DbzQ+3AkgGwymQO7aZFNP7ns2lZKGtvRBzRXfdi60= | ||||
| github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= | ||||
| github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM= | ||||
| @@ -11,6 +12,7 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF | ||||
| github.com/apache/thrift v0.13.0 h1:5hryIiq9gtn+MiLVn0wP37kb/uTeRZgN08WoCsAhIhI= | ||||
| github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= | ||||
| github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= | ||||
| github.com/benbjohnson/clock v1.0.0 h1:78Jk/r6m4wCi6sndMpty7A//t4dw/RW5fV4ZgDVfX1w= | ||||
| github.com/benbjohnson/clock v1.0.0/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= | ||||
| github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= | ||||
| github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= | ||||
| @@ -91,6 +93,7 @@ github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= | ||||
| github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= | ||||
| github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= | ||||
| github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= | ||||
| github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= | ||||
| github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= | ||||
| github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= | ||||
| github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= | ||||
|   | ||||
| @@ -14,6 +14,7 @@ cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiy | ||||
| dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= | ||||
| github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= | ||||
| github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= | ||||
| github.com/DataDog/sketches-go v0.0.0-20190923095040-43f19ad77ff7 h1:qELHH0AWCvf98Yf+CNIJx9vOZOfHFDDzgDRYsnNk/vs= | ||||
| github.com/DataDog/sketches-go v0.0.0-20190923095040-43f19ad77ff7/go.mod h1:Q5DbzQ+3AkgGwymQO7aZFNP7ns2lZKGtvRBzRXfdi60= | ||||
| github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= | ||||
| github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM= | ||||
| @@ -21,6 +22,7 @@ github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrU | ||||
| github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= | ||||
| github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= | ||||
| github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= | ||||
| github.com/benbjohnson/clock v1.0.0 h1:78Jk/r6m4wCi6sndMpty7A//t4dw/RW5fV4ZgDVfX1w= | ||||
| github.com/benbjohnson/clock v1.0.0/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= | ||||
| github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= | ||||
| github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= | ||||
| @@ -102,6 +104,7 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a | ||||
| github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= | ||||
| github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= | ||||
| github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= | ||||
| github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= | ||||
| github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= | ||||
| github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= | ||||
| github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= | ||||
|   | ||||
| @@ -16,6 +16,7 @@ package metric | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"sync" | ||||
|  | ||||
| 	"go.opentelemetry.io/otel/api/core" | ||||
| 	apimetric "go.opentelemetry.io/otel/api/metric" | ||||
| @@ -45,6 +46,11 @@ type ( | ||||
| 		Measurements []Measurement | ||||
| 	} | ||||
|  | ||||
| 	MeterProvider struct { | ||||
| 		lock       sync.Mutex | ||||
| 		registered map[string]*Meter | ||||
| 	} | ||||
|  | ||||
| 	Meter struct { | ||||
| 		MeasurementBatches []Batch | ||||
| 	} | ||||
| @@ -71,6 +77,9 @@ const ( | ||||
| ) | ||||
|  | ||||
| func (i *Instrument) AcquireHandle(labels apimetric.LabelSet) apimetric.HandleImpl { | ||||
| 	if ld, ok := labels.(apimetric.LabelSetDelegate); ok { | ||||
| 		labels = ld.Delegate() | ||||
| 	} | ||||
| 	return &Handle{ | ||||
| 		Instrument: i, | ||||
| 		LabelSet:   labels.(*LabelSet), | ||||
| @@ -78,6 +87,9 @@ func (i *Instrument) AcquireHandle(labels apimetric.LabelSet) apimetric.HandleIm | ||||
| } | ||||
|  | ||||
| func (i *Instrument) RecordOne(ctx context.Context, number core.Number, labels apimetric.LabelSet) { | ||||
| 	if ld, ok := labels.(apimetric.LabelSetDelegate); ok { | ||||
| 		labels = ld.Delegate() | ||||
| 	} | ||||
| 	doRecordBatch(ctx, labels.(*LabelSet), i, number) | ||||
| } | ||||
|  | ||||
| @@ -99,6 +111,24 @@ func (s *LabelSet) Meter() apimetric.Meter { | ||||
| 	return s.TheMeter | ||||
| } | ||||
|  | ||||
| func NewProvider() *MeterProvider { | ||||
| 	return &MeterProvider{ | ||||
| 		registered: map[string]*Meter{}, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (p *MeterProvider) Meter(name string) apimetric.Meter { | ||||
| 	p.lock.Lock() | ||||
| 	defer p.lock.Unlock() | ||||
|  | ||||
| 	if lookup, ok := p.registered[name]; ok { | ||||
| 		return lookup | ||||
| 	} | ||||
| 	m := NewMeter() | ||||
| 	p.registered[name] = m | ||||
| 	return m | ||||
| } | ||||
|  | ||||
| func NewMeter() *Meter { | ||||
| 	return &Meter{} | ||||
| } | ||||
|   | ||||
| @@ -281,6 +281,9 @@ func (m *SDK) Labels(kvs ...core.KeyValue) api.LabelSet { | ||||
| // 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 { | ||||
| 	if del, ok := ls.(api.LabelSetDelegate); ok { | ||||
| 		ls = del.Delegate() | ||||
| 	} | ||||
| 	if l, _ := ls.(*labels); l != nil && l.meter == m { | ||||
| 		return l | ||||
| 	} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user