Skip to content

Commit

Permalink
Global meter forwarding implementation (#392)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
jmacd authored Dec 24, 2019
1 parent 11f67ce commit 1414d36
Show file tree
Hide file tree
Showing 15 changed files with 790 additions and 30 deletions.
41 changes: 11 additions & 30 deletions api/global/global.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 102 additions & 0 deletions api/global/internal/benchmark_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
}
Loading

0 comments on commit 1414d36

Please sign in to comment.