diff --git a/CHANGELOG.md b/CHANGELOG.md index 876808ecac2..f79b64f2713 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Zipkin exporter helpers: pipeline methods introduced, new exporter method adjusted. (#944) - The trace (`go.opentelemetry.io/otel/exporters/trace/stdout`) and metric (`go.opentelemetry.io/otel/exporters/metric/stdout`) `stdout` exporters are now merged into a single exporter at `go.opentelemetry.io/otel/exporters/stdout`. (#956) +### Fixed + +- Remove default SDK dependencies from the `go.opentelemetry.io/otel/api` package. (#977) + ## [0.9.0] - 2020-07-20 ### Added diff --git a/api/global/internal/benchmark_test.go b/api/global/internal/benchmark_test.go index 107461cc1c1..d8e4ca932c4 100644 --- a/api/global/internal/benchmark_test.go +++ b/api/global/internal/benchmark_test.go @@ -21,48 +21,12 @@ import ( "go.opentelemetry.io/otel/api/global" "go.opentelemetry.io/otel/api/global/internal" "go.opentelemetry.io/otel/api/kv" - "go.opentelemetry.io/otel/api/metric" - "go.opentelemetry.io/otel/api/trace" - export "go.opentelemetry.io/otel/sdk/export/metric" - sdk "go.opentelemetry.io/otel/sdk/metric" - "go.opentelemetry.io/otel/sdk/metric/processor/test" - sdktrace "go.opentelemetry.io/otel/sdk/trace" ) -var Must = metric.Must - -// benchFixture is copied from sdk/metric/benchmark_test.go. -// TODO refactor to share this code. -type benchFixture struct { - export.AggregatorSelector - accumulator *sdk.Accumulator - meter metric.Meter - B *testing.B -} - -var _ metric.Provider = &benchFixture{} - -func newFixture(b *testing.B) *benchFixture { - b.ReportAllocs() - bf := &benchFixture{ - B: b, - AggregatorSelector: test.AggregatorSelector(), - } - - bf.accumulator = sdk.NewAccumulator(bf) - bf.meter = metric.WrapMeterImpl(bf.accumulator, "test") - return bf -} - -func (*benchFixture) Process(export.Accumulation) error { - return nil -} - -func (fix *benchFixture) Meter(_ string, _ ...metric.MeterOption) metric.Meter { - return fix.meter -} - func BenchmarkGlobalInt64CounterAddNoSDK(b *testing.B) { + // Compare with BenchmarkGlobalInt64CounterAddWithSDK() in + // ../../../sdk/metric/benchmark_test.go to see the overhead of the + // global no-op system against a registered SDK. internal.ResetForTest() ctx := context.Background() sdk := global.Meter("test") @@ -76,60 +40,15 @@ func BenchmarkGlobalInt64CounterAddNoSDK(b *testing.B) { } } -func BenchmarkGlobalInt64CounterAddWithSDK(b *testing.B) { - // Comapare with BenchmarkInt64CounterAdd() in ../../sdk/meter/benchmark_test.go +func BenchmarkStartEndSpanNoSDK(b *testing.B) { + // Compare with BenchmarkStartEndSpan() in + // ../../../sdk/trace/benchmark_test.go. + internal.ResetForTest() + t := global.Tracer("Benchmark StartEndSpan") ctx := context.Background() - fix := newFixture(b) - - sdk := global.Meter("test") - - global.SetMeterProvider(fix) - - labs := []kv.KeyValue{kv.String("A", "B")} - cnt := Must(sdk).NewInt64Counter("int64.counter") - b.ResetTimer() - for i := 0; i < b.N; i++ { - cnt.Add(ctx, 1, labs...) - } -} - -func BenchmarkStartEndSpan(b *testing.B) { - // Comapare with BenchmarkStartEndSpan() in ../../sdk/trace/benchmark_test.go - traceBenchmark(b, func(b *testing.B) { - t := global.Tracer("Benchmark StartEndSpan") - ctx := context.Background() - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, span := t.Start(ctx, "/foo") - span.End() - } - }) -} - -func traceBenchmark(b *testing.B, fn func(*testing.B)) { - internal.ResetForTest() - b.Run("No SDK", func(b *testing.B) { - b.ReportAllocs() - fn(b) - }) - b.Run("Default SDK (AlwaysSample)", func(b *testing.B) { - b.ReportAllocs() - global.SetTraceProvider(traceProvider(b, sdktrace.AlwaysSample())) - fn(b) - }) - b.Run("Default SDK (NeverSample)", func(b *testing.B) { - b.ReportAllocs() - global.SetTraceProvider(traceProvider(b, sdktrace.NeverSample())) - fn(b) - }) -} - -func traceProvider(b *testing.B, sampler sdktrace.Sampler) trace.Provider { - tp, err := sdktrace.NewProvider(sdktrace.WithConfig(sdktrace.Config{DefaultSampler: sampler})) - if err != nil { - b.Fatalf("Failed to create trace provider with sampler: %v", err) + _, span := t.Start(ctx, "/foo") + span.End() } - return tp } diff --git a/api/global/internal/meter_test.go b/api/global/internal/meter_test.go index aad2e3fabd2..aa962ba0188 100644 --- a/api/global/internal/meter_test.go +++ b/api/global/internal/meter_test.go @@ -28,6 +28,8 @@ import ( metrictest "go.opentelemetry.io/otel/internal/metric" ) +var Must = metric.Must + // Note: Maybe this should be factored into ../../../internal/metric? type measured struct { Name string diff --git a/api/global/internal/trace_test.go b/api/global/internal/trace_test.go index ac6ceb43b69..58d73aa5eb0 100644 --- a/api/global/internal/trace_test.go +++ b/api/global/internal/trace_test.go @@ -18,32 +18,14 @@ import ( "context" "testing" - "github.com/stretchr/testify/require" + "github.com/stretchr/testify/assert" "go.opentelemetry.io/otel/api/global" "go.opentelemetry.io/otel/api/global/internal" - export "go.opentelemetry.io/otel/sdk/export/trace" - sdktrace "go.opentelemetry.io/otel/sdk/trace" + "go.opentelemetry.io/otel/api/trace/testtrace" ) -type testSpanProcesor struct { - // Names of Spans started. - spansStarted []string - // Names of Spans ended. - spansEnded []string -} - -func (t *testSpanProcesor) OnStart(s *export.SpanData) { - t.spansStarted = append(t.spansStarted, s.Name) -} - -func (t *testSpanProcesor) OnEnd(s *export.SpanData) { - t.spansEnded = append(t.spansEnded, s.Name) -} - -func (t *testSpanProcesor) Shutdown() {} - -func TestTraceDefaultSDK(t *testing.T) { +func TestTraceWithSDK(t *testing.T) { internal.ResetForTest() ctx := context.Background() @@ -56,13 +38,8 @@ func TestTraceDefaultSDK(t *testing.T) { t.Errorf("failed to wrap function with span prior to initialization: %v", err) } - tp, err := sdktrace.NewProvider(sdktrace.WithConfig(sdktrace.Config{DefaultSampler: sdktrace.AlwaysSample()})) - if err != nil { - t.Fatal(err) - } - tsp := &testSpanProcesor{} - tp.RegisterSpanProcessor(tsp) - + sr := new(testtrace.StandardSpanRecorder) + tp := testtrace.NewProvider(testtrace.WithSpanRecorder(sr)) global.SetTraceProvider(tp) // This span was started before initialization, it is expected to be dropped. @@ -83,7 +60,14 @@ func TestTraceDefaultSDK(t *testing.T) { t.Errorf("failed to wrap function with span post initialization with new tracer: %v", err) } + filterNames := func(spans []*testtrace.Span) []string { + names := make([]string, len(spans)) + for i := range spans { + names[i] = spans[i].Name() + } + return names + } expected := []string{"span2", "withSpan2", "span3", "withSpan3"} - require.Equal(t, tsp.spansStarted, expected) - require.Equal(t, tsp.spansEnded, expected) + assert.ElementsMatch(t, expected, filterNames(sr.Started())) + assert.ElementsMatch(t, expected, filterNames(sr.Completed())) } diff --git a/api/kv/benchmark_test.go b/api/kv/benchmark_test.go index 33aa93424f7..a1fea5b290d 100644 --- a/api/kv/benchmark_test.go +++ b/api/kv/benchmark_test.go @@ -15,62 +15,360 @@ package kv_test import ( - "context" "testing" "go.opentelemetry.io/otel/api/kv" +) + +type test struct{} + +var ( + arrayVal = []string{"one", "two"} + arrayKeyVal = kv.Array("array", arrayVal) + + boolVal = true + boolKeyVal = kv.Bool("bool", boolVal) + + intVal = int(1) + intKeyVal = kv.Int("int", intVal) + + int8Val = int8(1) + int8KeyVal = kv.Int("int8", int(int8Val)) + + int16Val = int16(1) + int16KeyVal = kv.Int("int16", int(int16Val)) + + int32Val = int32(1) + int32KeyVal = kv.Int32("int32", int32Val) + + int64Val = int64(1) + int64KeyVal = kv.Int64("int64", int64Val) + + uintVal = uint(1) + uintKeyVal = kv.Uint("uint", uintVal) + + uint8Val = uint8(1) + uint8KeyVal = kv.Uint("uint8", uint(uint8Val)) + + uint16Val = uint16(1) + uint16KeyVal = kv.Uint("uint16", uint(uint16Val)) + + uint32Val = uint32(1) + uint32KeyVal = kv.Uint32("uint32", uint32Val) - "go.opentelemetry.io/otel/api/global" - "go.opentelemetry.io/otel/api/trace" - sdktrace "go.opentelemetry.io/otel/sdk/trace" + uint64Val = uint64(1) + uint64KeyVal = kv.Uint64("uint64", uint64Val) + + float32Val = float32(1.0) + float32KeyVal = kv.Float32("float32", float32Val) + + float64Val = float64(1.0) + float64KeyVal = kv.Float64("float64", float64Val) + + stringVal = "string" + stringKeyVal = kv.String("string", stringVal) + + bytesVal = []byte("bytes") + structVal = test{} ) -// Note: The tests below load a real SDK to ensure the compiler isn't optimizing -// the test based on global analysis limited to the NoopSpan implementation. -func getSpan() trace.Span { - _, sp := global.Tracer("Test").Start(context.Background(), "Span") - tr, _ := sdktrace.NewProvider() - global.SetTraceProvider(tr) - return sp +func BenchmarkArrayKey(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Array("array", arrayVal) + } +} + +func BenchmarkArrayKeyAny(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Any("array", arrayVal) + } +} + +func BenchmarkBoolKey(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Bool("bool", boolVal) + } +} + +func BenchmarkBoolKeyAny(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Any("bool", boolVal) + } +} + +func BenchmarkIntKey(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Int("int", intVal) + } +} + +func BenchmarkIntKeyAny(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Any("int", intVal) + } +} + +func BenchmarkInt8KeyAny(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Any("int8", int8Val) + } +} + +func BenchmarkInt16KeyAny(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Any("int16", int16Val) + } +} + +func BenchmarkInt32Key(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Int32("int32", int32Val) + } +} + +func BenchmarkInt32KeyAny(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Any("int32", int32Val) + } +} + +func BenchmarkInt64Key(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Int64("int64", int64Val) + } +} + +func BenchmarkInt64KeyAny(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Any("int64", int64Val) + } +} + +func BenchmarkUintKey(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Uint("uint", uintVal) + } +} + +func BenchmarkUintKeyAny(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Any("uint", uintVal) + } +} + +func BenchmarkUint8KeyAny(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Any("uint8", uint8Val) + } +} + +func BenchmarkUint16KeyAny(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Any("uint16", uint16Val) + } +} + +func BenchmarkUint32Key(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Uint32("uint32", uint32Val) + } +} + +func BenchmarkUint32KeyAny(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Any("uint32", uint32Val) + } +} + +func BenchmarkUint64Key(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Uint64("uint64", uint64Val) + } +} + +func BenchmarkUint64KeyAny(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Any("uint64", uint64Val) + } +} + +func BenchmarkFloat32Key(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Float32("float32", float32Val) + } +} + +func BenchmarkFloat32KeyAny(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Any("float32", float32Val) + } +} + +func BenchmarkFloat64Key(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Float64("float64", float64Val) + } +} + +func BenchmarkFloat64KeyAny(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Any("float64", float64Val) + } +} + +func BenchmarkStringKey(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.String("string", stringVal) + } +} + +func BenchmarkStringKeyAny(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Any("string", stringVal) + } +} + +func BenchmarkBytesKeyAny(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Any("bytes", bytesVal) + } +} + +func BenchmarkStructKeyAny(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Any("struct", structVal) + } } -func BenchmarkKeyAny(b *testing.B) { +func BenchmarkEmitArray(b *testing.B) { + b.ReportAllocs() for i := 0; i < b.N; i++ { - kv.Any("Attr", int(256)) + _ = arrayKeyVal.Value.Emit() } } -func BenchmarkMultiNoKeyInference(b *testing.B) { - sp := getSpan() +func BenchmarkEmitBool(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = boolKeyVal.Value.Emit() + } +} +func BenchmarkEmitInt(b *testing.B) { b.ReportAllocs() - b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = intKeyVal.Value.Emit() + } +} +func BenchmarkEmitInt8(b *testing.B) { + b.ReportAllocs() for i := 0; i < b.N; i++ { - sp.SetAttributes(kv.Int("Attr", 1)) + _ = int8KeyVal.Value.Emit() } } -func BenchmarkMultiWithKeyInference(b *testing.B) { - sp := getSpan() +func BenchmarkEmitInt16(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = int16KeyVal.Value.Emit() + } +} +func BenchmarkEmitInt32(b *testing.B) { b.ReportAllocs() - b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = int32KeyVal.Value.Emit() + } +} +func BenchmarkEmitInt64(b *testing.B) { + b.ReportAllocs() for i := 0; i < b.N; i++ { - sp.SetAttributes(kv.Any("Attr", 1)) + _ = int64KeyVal.Value.Emit() } } -func BenchmarkSingleWithKeyInferenceValue(b *testing.B) { - sp := getSpan() +func BenchmarkEmitUint(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = uintKeyVal.Value.Emit() + } +} +func BenchmarkEmitUint8(b *testing.B) { b.ReportAllocs() - b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = uint8KeyVal.Value.Emit() + } +} +func BenchmarkEmitUint16(b *testing.B) { + b.ReportAllocs() for i := 0; i < b.N; i++ { - sp.SetAttribute("Attr", 1) + _ = uint16KeyVal.Value.Emit() } +} + +func BenchmarkEmitUint32(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = uint32KeyVal.Value.Emit() + } +} - b.StopTimer() +func BenchmarkEmitUint64(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = uint64KeyVal.Value.Emit() + } +} + +func BenchmarkEmitFloat32(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = float32KeyVal.Value.Emit() + } +} + +func BenchmarkEmitFloat64(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = float64KeyVal.Value.Emit() + } +} + +func BenchmarkEmitString(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = stringKeyVal.Value.Emit() + } } diff --git a/api/kv/key_test.go b/api/kv/key_test.go index f8c7311c6d3..d79031ff947 100644 --- a/api/kv/key_test.go +++ b/api/kv/key_test.go @@ -119,43 +119,3 @@ func TestEmit(t *testing.T) { }) } } - -func BenchmarkEmitBool(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - n := kv.BoolValue(i%2 == 0) - _ = n.Emit() - } -} - -func BenchmarkEmitInt64(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - n := kv.Int64Value(int64(i)) - _ = n.Emit() - } -} - -func BenchmarkEmitUInt64(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - n := kv.Uint64Value(uint64(i)) - _ = n.Emit() - } -} - -func BenchmarkEmitFloat64(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - n := kv.Float64Value(float64(i)) - _ = n.Emit() - } -} - -func BenchmarkEmitFloat32(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - n := kv.Float32Value(float32(i)) - _ = n.Emit() - } -} diff --git a/api/trace/testtrace/config.go b/api/trace/testtrace/config.go new file mode 100644 index 00000000000..b4557676c18 --- /dev/null +++ b/api/trace/testtrace/config.go @@ -0,0 +1,134 @@ +// Copyright The 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 testtrace + +import ( + "context" + "encoding/binary" + "sync" + "sync/atomic" + + "go.opentelemetry.io/otel/api/trace" +) + +// defaultSpanContextFunc returns the default SpanContextFunc. +func defaultSpanContextFunc() func(context.Context) trace.SpanContext { + var traceID, spanID uint64 = 1, 1 + return func(ctx context.Context) trace.SpanContext { + var sc trace.SpanContext + if lsc := trace.SpanFromContext(ctx).SpanContext(); lsc.IsValid() { + sc = lsc + } else if rsc := trace.RemoteSpanContextFromContext(ctx); rsc.IsValid() { + sc = rsc + } else { + binary.BigEndian.PutUint64(sc.TraceID[:], atomic.AddUint64(&traceID, 1)) + } + binary.BigEndian.PutUint64(sc.SpanID[:], atomic.AddUint64(&spanID, 1)) + return sc + } +} + +type config struct { + // SpanContextFunc returns a SpanContext from an parent Context for a + // new span. + SpanContextFunc func(context.Context) trace.SpanContext + + // SpanRecorder keeps track of spans. + SpanRecorder SpanRecorder +} + +func configure(opts ...Option) config { + conf := config{} + for _, opt := range opts { + opt.Apply(&conf) + } + if conf.SpanContextFunc == nil { + conf.SpanContextFunc = defaultSpanContextFunc() + } + return conf +} + +type Option interface { + Apply(*config) +} + +type spanContextFuncOption struct { + SpanContextFunc func(context.Context) trace.SpanContext +} + +func (o spanContextFuncOption) Apply(c *config) { + c.SpanContextFunc = o.SpanContextFunc +} + +func WithSpanContextFunc(f func(context.Context) trace.SpanContext) Option { + return spanContextFuncOption{f} +} + +type spanRecorderOption struct { + SpanRecorder SpanRecorder +} + +func (o spanRecorderOption) Apply(c *config) { + c.SpanRecorder = o.SpanRecorder +} + +func WithSpanRecorder(sr SpanRecorder) Option { + return spanRecorderOption{sr} +} + +type SpanRecorder interface { + OnStart(*Span) + OnEnd(*Span) +} + +type StandardSpanRecorder struct { + startedMu sync.RWMutex + started []*Span + + doneMu sync.RWMutex + done []*Span +} + +func (ssr *StandardSpanRecorder) OnStart(span *Span) { + ssr.startedMu.Lock() + defer ssr.startedMu.Unlock() + ssr.started = append(ssr.started, span) +} + +func (ssr *StandardSpanRecorder) OnEnd(span *Span) { + ssr.doneMu.Lock() + defer ssr.doneMu.Unlock() + ssr.done = append(ssr.done, span) +} + +func (ssr *StandardSpanRecorder) Started() []*Span { + ssr.startedMu.RLock() + defer ssr.startedMu.RUnlock() + started := make([]*Span, len(ssr.started)) + for i := range ssr.started { + started[i] = ssr.started[i] + } + return started +} + +func (ssr *StandardSpanRecorder) Completed() []*Span { + ssr.doneMu.RLock() + defer ssr.doneMu.RUnlock() + done := make([]*Span, len(ssr.done)) + for i := range ssr.done { + done[i] = ssr.done[i] + } + return done +} diff --git a/api/trace/testtrace/generator.go b/api/trace/testtrace/generator.go deleted file mode 100644 index 35a09b5e8b1..00000000000 --- a/api/trace/testtrace/generator.go +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright The 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 testtrace - -import ( - "encoding/binary" - "sync" - - "go.opentelemetry.io/otel/api/trace" -) - -type Generator interface { - TraceID() trace.ID - SpanID() trace.SpanID -} - -var _ Generator = (*CountGenerator)(nil) - -// CountGenerator is a simple Generator that can be used to create unique, albeit deterministic, -// trace and span IDs. -type CountGenerator struct { - lock sync.Mutex - traceIDHigh uint64 - traceIDLow uint64 - spanID uint64 -} - -func NewCountGenerator() *CountGenerator { - return &CountGenerator{} -} - -func (g *CountGenerator) TraceID() trace.ID { - g.lock.Lock() - defer g.lock.Unlock() - - if g.traceIDHigh == g.traceIDLow { - g.traceIDHigh++ - } else { - g.traceIDLow++ - } - - var traceID trace.ID - - binary.BigEndian.PutUint64(traceID[0:8], g.traceIDLow) - binary.BigEndian.PutUint64(traceID[8:], g.traceIDHigh) - - return traceID -} - -func (g *CountGenerator) SpanID() trace.SpanID { - g.lock.Lock() - defer g.lock.Unlock() - - g.spanID++ - - var spanID trace.SpanID - - binary.BigEndian.PutUint64(spanID[:], g.spanID) - - return spanID -} diff --git a/api/trace/testtrace/provider.go b/api/trace/testtrace/provider.go new file mode 100644 index 00000000000..9bd58daf226 --- /dev/null +++ b/api/trace/testtrace/provider.go @@ -0,0 +1,64 @@ +// Copyright The 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 testtrace + +import ( + "sync" + + "go.opentelemetry.io/otel/api/trace" +) + +type Provider struct { + config config + + tracersMu sync.Mutex + tracers map[instrumentation]*Tracer +} + +var _ trace.Provider = (*Provider)(nil) + +func NewProvider(opts ...Option) *Provider { + return &Provider{ + config: configure(opts...), + tracers: make(map[instrumentation]*Tracer), + } +} + +type instrumentation struct { + Name, Version string +} + +func (p *Provider) Tracer(instName string, opts ...trace.TracerOption) trace.Tracer { + conf := new(trace.TracerConfig) + for _, o := range opts { + o(conf) + } + inst := instrumentation{ + Name: instName, + Version: conf.InstrumentationVersion, + } + p.tracersMu.Lock() + defer p.tracersMu.Unlock() + t, ok := p.tracers[inst] + if !ok { + t = &Tracer{ + Name: instName, + Version: conf.InstrumentationVersion, + config: &p.config, + } + p.tracers[inst] = t + } + return t +} diff --git a/api/trace/testtrace/span.go b/api/trace/testtrace/span.go index ea39dc61b9e..38dd84085c1 100644 --- a/api/trace/testtrace/span.go +++ b/api/trace/testtrace/span.go @@ -36,7 +36,7 @@ const ( var _ trace.Span = (*Span)(nil) type Span struct { - lock *sync.RWMutex + lock sync.RWMutex tracer *Tracer spanContext trace.SpanContext parentSpanID trace.SpanID @@ -64,7 +64,6 @@ func (s *Span) End(opts ...trace.EndOption) { } var c trace.EndConfig - for _, opt := range opts { opt(&c) } @@ -76,6 +75,9 @@ func (s *Span) End(opts ...trace.EndOption) { } s.ended = true + if s.tracer.config.SpanRecorder != nil { + s.tracer.config.SpanRecorder.OnEnd(s) + } } func (s *Span) RecordError(ctx context.Context, err error, opts ...trace.ErrorOption) { diff --git a/api/trace/testtrace/span_test.go b/api/trace/testtrace/span_test.go index e5229e24614..33cd23f6f6b 100644 --- a/api/trace/testtrace/span_test.go +++ b/api/trace/testtrace/span_test.go @@ -33,12 +33,13 @@ import ( func TestSpan(t *testing.T) { t.Run("#Tracer", func(t *testing.T) { + tp := testtrace.NewProvider() t.Run("returns the tracer used to start the span", func(t *testing.T) { t.Parallel() e := matchers.NewExpecter(t) - tracer := testtrace.NewTracer() + tracer := tp.Tracer(t.Name()) _, subject := tracer.Start(context.Background(), "test") e.Expect(subject.Tracer()).ToEqual(tracer) @@ -46,12 +47,13 @@ func TestSpan(t *testing.T) { }) t.Run("#End", func(t *testing.T) { + tp := testtrace.NewProvider() t.Run("ends the span", func(t *testing.T) { t.Parallel() e := matchers.NewExpecter(t) - tracer := testtrace.NewTracer() + tracer := tp.Tracer(t.Name()) _, span := tracer.Start(context.Background(), "test") subject, ok := span.(*testtrace.Span) @@ -82,7 +84,7 @@ func TestSpan(t *testing.T) { e := matchers.NewExpecter(t) - tracer := testtrace.NewTracer() + tracer := tp.Tracer(t.Name()) _, span := tracer.Start(context.Background(), "test") subject, ok := span.(*testtrace.Span) @@ -105,7 +107,7 @@ func TestSpan(t *testing.T) { e := matchers.NewExpecter(t) - tracer := testtrace.NewTracer() + tracer := tp.Tracer(t.Name()) _, span := tracer.Start(context.Background(), "test") subject, ok := span.(*testtrace.Span) @@ -124,6 +126,7 @@ func TestSpan(t *testing.T) { }) t.Run("#RecordError", func(t *testing.T) { + tp := testtrace.NewProvider() t.Run("records an error", func(t *testing.T) { t.Parallel() @@ -147,7 +150,7 @@ func TestSpan(t *testing.T) { for _, s := range scenarios { e := matchers.NewExpecter(t) - tracer := testtrace.NewTracer() + tracer := tp.Tracer(t.Name()) ctx, span := tracer.Start(context.Background(), "test") subject, ok := span.(*testtrace.Span) @@ -175,7 +178,7 @@ func TestSpan(t *testing.T) { e := matchers.NewExpecter(t) - tracer := testtrace.NewTracer() + tracer := tp.Tracer(t.Name()) ctx, span := tracer.Start(context.Background(), "test") subject, ok := span.(*testtrace.Span) @@ -204,7 +207,7 @@ func TestSpan(t *testing.T) { e := matchers.NewExpecter(t) - tracer := testtrace.NewTracer() + tracer := tp.Tracer(t.Name()) ctx, span := tracer.Start(context.Background(), "test") subject, ok := span.(*testtrace.Span) @@ -221,7 +224,7 @@ func TestSpan(t *testing.T) { e := matchers.NewExpecter(t) - tracer := testtrace.NewTracer() + tracer := tp.Tracer(t.Name()) ctx, span := tracer.Start(context.Background(), "test") subject, ok := span.(*testtrace.Span) @@ -234,12 +237,13 @@ func TestSpan(t *testing.T) { }) t.Run("#IsRecording", func(t *testing.T) { + tp := testtrace.NewProvider() t.Run("returns true", func(t *testing.T) { t.Parallel() e := matchers.NewExpecter(t) - tracer := testtrace.NewTracer() + tracer := tp.Tracer(t.Name()) _, subject := tracer.Start(context.Background(), "test") e.Expect(subject.IsRecording()).ToBeTrue() @@ -247,12 +251,13 @@ func TestSpan(t *testing.T) { }) t.Run("#SpanContext", func(t *testing.T) { + tp := testtrace.NewProvider() t.Run("returns a valid SpanContext", func(t *testing.T) { t.Parallel() e := matchers.NewExpecter(t) - tracer := testtrace.NewTracer() + tracer := tp.Tracer(t.Name()) _, subject := tracer.Start(context.Background(), "test") e.Expect(subject.SpanContext().IsValid()).ToBeTrue() @@ -263,7 +268,7 @@ func TestSpan(t *testing.T) { e := matchers.NewExpecter(t) - tracer := testtrace.NewTracer() + tracer := tp.Tracer(t.Name()) _, subject := tracer.Start(context.Background(), "test") e.Expect(subject.SpanContext()).ToEqual(subject.SpanContext()) @@ -271,12 +276,13 @@ func TestSpan(t *testing.T) { }) t.Run("#Name", func(t *testing.T) { + tp := testtrace.NewProvider() t.Run("returns the most recently set name on the span", func(t *testing.T) { t.Parallel() e := matchers.NewExpecter(t) - tracer := testtrace.NewTracer() + tracer := tp.Tracer(t.Name()) originalName := "test" _, span := tracer.Start(context.Background(), originalName) @@ -299,7 +305,7 @@ func TestSpan(t *testing.T) { e := matchers.NewExpecter(t) - tracer := testtrace.NewTracer() + tracer := tp.Tracer(t.Name()) originalName := "test" _, span := tracer.Start(context.Background(), originalName) @@ -314,12 +320,13 @@ func TestSpan(t *testing.T) { }) t.Run("#Attributes", func(t *testing.T) { + tp := testtrace.NewProvider() t.Run("returns an empty map by default", func(t *testing.T) { t.Parallel() e := matchers.NewExpecter(t) - tracer := testtrace.NewTracer() + tracer := tp.Tracer(t.Name()) _, span := tracer.Start(context.Background(), "test") subject, ok := span.(*testtrace.Span) @@ -333,7 +340,7 @@ func TestSpan(t *testing.T) { e := matchers.NewExpecter(t) - tracer := testtrace.NewTracer() + tracer := tp.Tracer(t.Name()) _, span := tracer.Start(context.Background(), "test") subject, ok := span.(*testtrace.Span) @@ -359,7 +366,7 @@ func TestSpan(t *testing.T) { e := matchers.NewExpecter(t) - tracer := testtrace.NewTracer() + tracer := tp.Tracer(t.Name()) _, span := tracer.Start(context.Background(), "test") subject, ok := span.(*testtrace.Span) @@ -382,7 +389,7 @@ func TestSpan(t *testing.T) { e := matchers.NewExpecter(t) - tracer := testtrace.NewTracer() + tracer := tp.Tracer(t.Name()) _, span := tracer.Start(context.Background(), "test") subject, ok := span.(*testtrace.Span) @@ -409,12 +416,13 @@ func TestSpan(t *testing.T) { }) t.Run("#Links", func(t *testing.T) { + tp := testtrace.NewProvider() t.Run("returns an empty map by default", func(t *testing.T) { t.Parallel() e := matchers.NewExpecter(t) - tracer := testtrace.NewTracer() + tracer := tp.Tracer(t.Name()) _, span := tracer.Start(context.Background(), "test") subject, ok := span.(*testtrace.Span) @@ -425,12 +433,13 @@ func TestSpan(t *testing.T) { }) t.Run("#Events", func(t *testing.T) { + tp := testtrace.NewProvider() t.Run("returns an empty slice by default", func(t *testing.T) { t.Parallel() e := matchers.NewExpecter(t) - tracer := testtrace.NewTracer() + tracer := tp.Tracer(t.Name()) _, span := tracer.Start(context.Background(), "test") subject, ok := span.(*testtrace.Span) @@ -444,7 +453,7 @@ func TestSpan(t *testing.T) { e := matchers.NewExpecter(t) - tracer := testtrace.NewTracer() + tracer := tp.Tracer(t.Name()) _, span := tracer.Start(context.Background(), "test") subject, ok := span.(*testtrace.Span) @@ -497,7 +506,7 @@ func TestSpan(t *testing.T) { e := matchers.NewExpecter(t) - tracer := testtrace.NewTracer() + tracer := tp.Tracer(t.Name()) _, span := tracer.Start(context.Background(), "test") subject, ok := span.(*testtrace.Span) @@ -518,12 +527,13 @@ func TestSpan(t *testing.T) { }) t.Run("#Status", func(t *testing.T) { + tp := testtrace.NewProvider() t.Run("defaults to OK", func(t *testing.T) { t.Parallel() e := matchers.NewExpecter(t) - tracer := testtrace.NewTracer() + tracer := tp.Tracer(t.Name()) _, span := tracer.Start(context.Background(), "test") subject, ok := span.(*testtrace.Span) @@ -562,7 +572,7 @@ func TestSpan(t *testing.T) { e := matchers.NewExpecter(t) - tracer := testtrace.NewTracer() + tracer := tp.Tracer(t.Name()) _, span := tracer.Start(context.Background(), "test") subject, ok := span.(*testtrace.Span) @@ -580,7 +590,7 @@ func TestSpan(t *testing.T) { e := matchers.NewExpecter(t) - tracer := testtrace.NewTracer() + tracer := tp.Tracer(t.Name()) _, span := tracer.Start(context.Background(), "test") subject, ok := span.(*testtrace.Span) diff --git a/api/trace/testtrace/tracer.go b/api/trace/testtrace/tracer.go index fe383a672df..5408576e206 100644 --- a/api/trace/testtrace/tracer.go +++ b/api/trace/testtrace/tracer.go @@ -16,132 +16,79 @@ package testtrace import ( "context" - "sync" "time" "go.opentelemetry.io/otel/api/kv" "go.opentelemetry.io/otel/api/trace" - - "go.opentelemetry.io/otel/internal/trace/parent" ) var _ trace.Tracer = (*Tracer)(nil) -// Tracer is a type of OpenTelemetry Tracer that tracks both active and ended spans, -// and which creates Spans that may be inspected to see what data has been set on them. +// Tracer is an OpenTelemetry Tracer implementation used for testing. type Tracer struct { - lock *sync.RWMutex - generator Generator - spans []*Span -} - -func NewTracer(opts ...TracerOption) *Tracer { - c := newTracerConfig(opts...) + // Name is the instrumentation name. + Name string + // Version is the instrumentation version. + Version string - return &Tracer{ - lock: &sync.RWMutex{}, - generator: c.generator, - } + config *config } func (t *Tracer) Start(ctx context.Context, name string, opts ...trace.StartOption) (context.Context, trace.Span) { var c trace.StartConfig - for _, opt := range opts { opt(&c) } - var traceID trace.ID - var parentSpanID trace.SpanID - - parentSpanContext, _, links := parent.GetSpanContextAndLinks(ctx, c.NewRoot) - - if parentSpanContext.IsValid() { - traceID = parentSpanContext.TraceID - parentSpanID = parentSpanContext.SpanID - } else { - traceID = t.generator.TraceID() - } - - spanID := t.generator.SpanID() - startTime := time.Now() - if st := c.StartTime; !st.IsZero() { startTime = st } span := &Span{ - lock: &sync.RWMutex{}, - tracer: t, - startTime: startTime, - spanContext: trace.SpanContext{ - TraceID: traceID, - SpanID: spanID, - }, - parentSpanID: parentSpanID, - attributes: make(map[kv.Key]kv.Value), - links: make(map[trace.SpanContext][]kv.KeyValue), + tracer: t, + startTime: startTime, + attributes: make(map[kv.Key]kv.Value), + links: make(map[trace.SpanContext][]kv.KeyValue), } - span.SetName(name) - span.SetAttributes(c.Attributes...) + if c.NewRoot { + span.spanContext = trace.EmptySpanContext() - for _, link := range links { - span.links[link.SpanContext] = link.Attributes + iodKey := kv.Key("ignored-on-demand") + if lsc := trace.SpanFromContext(ctx).SpanContext(); lsc.IsValid() { + span.links[lsc] = []kv.KeyValue{iodKey.String("current")} + } + if rsc := trace.RemoteSpanContextFromContext(ctx); rsc.IsValid() { + span.links[rsc] = []kv.KeyValue{iodKey.String("remote")} + } + } else { + span.spanContext = t.config.SpanContextFunc(ctx) + if lsc := trace.SpanFromContext(ctx).SpanContext(); lsc.IsValid() { + span.spanContext.TraceID = lsc.TraceID + span.parentSpanID = lsc.SpanID + } else if rsc := trace.RemoteSpanContextFromContext(ctx); rsc.IsValid() { + span.spanContext.TraceID = rsc.TraceID + span.parentSpanID = rsc.SpanID + } } + for _, link := range c.Links { span.links[link.SpanContext] = link.Attributes } - t.lock.Lock() - - t.spans = append(t.spans, span) - - t.lock.Unlock() + span.SetName(name) + span.SetAttributes(c.Attributes...) + if t.config.SpanRecorder != nil { + t.config.SpanRecorder.OnStart(span) + } return trace.ContextWithSpan(ctx, span), span } func (t *Tracer) WithSpan(ctx context.Context, name string, body func(ctx context.Context) error, opts ...trace.StartOption) error { - ctx, _ = t.Start(ctx, name, opts...) + ctx, span := t.Start(ctx, name, opts...) + defer span.End() return body(ctx) } - -// Spans returns the list of current and ended Spans started via the Tracer. -func (t *Tracer) Spans() []*Span { - t.lock.RLock() - defer t.lock.RUnlock() - - return append([]*Span{}, t.spans...) -} - -// TracerOption enables configuration of a new Tracer. -type TracerOption func(*tracerConfig) - -// TracerWithGenerator enables customization of the Generator that the Tracer will use -// to create new trace and span IDs. -// By default, new Tracers will use the CountGenerator. -func TracerWithGenerator(generator Generator) TracerOption { - return func(c *tracerConfig) { - c.generator = generator - } -} - -type tracerConfig struct { - generator Generator -} - -func newTracerConfig(opts ...TracerOption) tracerConfig { - var c tracerConfig - defaultOpts := []TracerOption{ - TracerWithGenerator(NewCountGenerator()), - } - - for _, opt := range append(defaultOpts, opts...) { - opt(&c) - } - - return c -} diff --git a/api/trace/testtrace/tracer_test.go b/api/trace/testtrace/tracer_test.go index fff385e1d9a..13b2fbe89d0 100644 --- a/api/trace/testtrace/tracer_test.go +++ b/api/trace/testtrace/tracer_test.go @@ -16,7 +16,9 @@ package testtrace_test import ( "context" + "fmt" "sync" + "sync/atomic" "testing" "time" @@ -28,9 +30,15 @@ import ( ) func TestTracer(t *testing.T) { - testharness.NewHarness(t).TestTracer(func() trace.Tracer { - return testtrace.NewTracer() - }) + tp := testtrace.NewProvider() + + testharness.NewHarness(t).TestTracer(func() func() trace.Tracer { + tp := testtrace.NewProvider() + var i uint64 + return func() trace.Tracer { + return tp.Tracer(fmt.Sprintf("tracer %d", atomic.AddUint64(&i, 1))) + } + }()) t.Run("#Start", func(t *testing.T) { testTracedSpan(t, func(tracer trace.Tracer, name string) (trace.Span, error) { @@ -46,7 +54,7 @@ func TestTracer(t *testing.T) { expectedStartTime := time.Now().AddDate(5, 0, 0) - subject := testtrace.NewTracer() + subject := tp.Tracer(t.Name()) _, span := subject.Start(context.Background(), "test", trace.WithStartTime(expectedStartTime)) testSpan, ok := span.(*testtrace.Span) @@ -63,7 +71,7 @@ func TestTracer(t *testing.T) { attr1 := kv.String("a", "1") attr2 := kv.String("b", "2") - subject := testtrace.NewTracer() + subject := tp.Tracer(t.Name()) _, span := subject.Start(context.Background(), "test", trace.WithAttributes(attr1, attr2)) testSpan, ok := span.(*testtrace.Span) @@ -79,7 +87,7 @@ func TestTracer(t *testing.T) { e := matchers.NewExpecter(t) - subject := testtrace.NewTracer() + subject := tp.Tracer(t.Name()) parent, parentSpan := subject.Start(context.Background(), "parent") parentSpanContext := parentSpan.SpanContext() @@ -100,7 +108,7 @@ func TestTracer(t *testing.T) { e := matchers.NewExpecter(t) - subject := testtrace.NewTracer() + subject := tp.Tracer(t.Name()) parent, parentSpan := subject.Start(context.Background(), "parent") _, remoteParentSpan := subject.Start(context.Background(), "remote not-a-parent") @@ -123,7 +131,7 @@ func TestTracer(t *testing.T) { e := matchers.NewExpecter(t) - subject := testtrace.NewTracer() + subject := tp.Tracer(t.Name()) _, remoteParentSpan := subject.Start(context.Background(), "remote parent") parent := trace.ContextWithRemoteSpanContext(context.Background(), remoteParentSpan.SpanContext()) @@ -145,7 +153,7 @@ func TestTracer(t *testing.T) { e := matchers.NewExpecter(t) - subject := testtrace.NewTracer() + subject := tp.Tracer(t.Name()) _, parentSpan := subject.Start(context.Background(), "not-a-parent") _, remoteParentSpan := subject.Start(context.Background(), "remote not-a-parent") @@ -170,7 +178,7 @@ func TestTracer(t *testing.T) { e := matchers.NewExpecter(t) - subject := testtrace.NewTracer() + subject := tp.Tracer(t.Name()) parentCtx, parentSpan := subject.Start(context.Background(), "not-a-parent") _, remoteParentSpan := subject.Start(context.Background(), "remote not-a-parent") @@ -220,7 +228,7 @@ func TestTracer(t *testing.T) { e := matchers.NewExpecter(t) - subject := testtrace.NewTracer() + subject := tp.Tracer(t.Name()) _, span := subject.Start(context.Background(), "link1") link1 := trace.Link{ @@ -270,7 +278,7 @@ func TestTracer(t *testing.T) { attr1 := kv.String("a", "1") attr2 := kv.String("b", "2") - subject := testtrace.NewTracer() + subject := tp.Tracer(t.Name()) var span trace.Span err := subject.WithSpan(context.Background(), "test", func(ctx context.Context) error { span = trace.SpanFromContext(ctx) @@ -291,12 +299,13 @@ func TestTracer(t *testing.T) { } func testTracedSpan(t *testing.T, fn func(tracer trace.Tracer, name string) (trace.Span, error)) { + tp := testtrace.NewProvider() t.Run("starts a span with the expected name", func(t *testing.T) { t.Parallel() e := matchers.NewExpecter(t) - subject := testtrace.NewTracer() + subject := tp.Tracer(t.Name()) expectedName := "test name" span, err := fn(subject, expectedName) @@ -314,7 +323,7 @@ func testTracedSpan(t *testing.T, fn func(tracer trace.Tracer, name string) (tra e := matchers.NewExpecter(t) - subject := testtrace.NewTracer() + subject := tp.Tracer(t.Name()) start := time.Now() span, err := fn(subject, "test") @@ -329,20 +338,21 @@ func testTracedSpan(t *testing.T, fn func(tracer trace.Tracer, name string) (tra e.Expect(testSpan.StartTime()).ToBeTemporally(matchers.BeforeOrSameTime, end) }) - t.Run("appends the span to the list of Spans", func(t *testing.T) { + t.Run("calls SpanRecorder.OnStart", func(t *testing.T) { t.Parallel() e := matchers.NewExpecter(t) - subject := testtrace.NewTracer() + sr := new(testtrace.StandardSpanRecorder) + subject := testtrace.NewProvider(testtrace.WithSpanRecorder(sr)).Tracer(t.Name()) subject.Start(context.Background(), "span1") - e.Expect(len(subject.Spans())).ToEqual(1) + e.Expect(len(sr.Started())).ToEqual(1) span, err := fn(subject, "span2") e.Expect(err).ToBeNil() - spans := subject.Spans() + spans := sr.Started() e.Expect(len(spans)).ToEqual(2) e.Expect(spans[1]).ToEqual(span) @@ -353,7 +363,8 @@ func testTracedSpan(t *testing.T, fn func(tracer trace.Tracer, name string) (tra e := matchers.NewExpecter(t) - subject := testtrace.NewTracer() + sr := new(testtrace.StandardSpanRecorder) + subject := testtrace.NewProvider(testtrace.WithSpanRecorder(sr)).Tracer(t.Name()) numSpans := 2 @@ -372,6 +383,6 @@ func testTracedSpan(t *testing.T, fn func(tracer trace.Tracer, name string) (tra wg.Wait() - e.Expect(len(subject.Spans())).ToEqual(numSpans) + e.Expect(len(sr.Started())).ToEqual(numSpans) }) } diff --git a/instrumentation/othttp/transport_example_test.go b/instrumentation/othttp/transport_example_test.go index f31f50aeef7..9254bd19024 100644 --- a/instrumentation/othttp/transport_example_test.go +++ b/instrumentation/othttp/transport_example_test.go @@ -15,45 +15,13 @@ package othttp import ( - "fmt" - "io/ioutil" - "log" "net/http" - - "go.opentelemetry.io/otel/api/global" - sdktrace "go.opentelemetry.io/otel/sdk/trace" ) func ExampleNewTransport() { - // Start with a working trace provider - tp, err := sdktrace.NewProvider(sdktrace.WithConfig(sdktrace.Config{DefaultSampler: sdktrace.AlwaysSample()})) - if err != nil { - log.Fatal(err) - } - global.SetTraceProvider(tp) - // Create an http.Client that uses the othttp.Transport // wrapped around the http.DefaultTransport - client := http.Client{ + _ = http.Client{ Transport: NewTransport(http.DefaultTransport), } - - // Make a request with our tracing client - response, err := client.Get("https://postman-echo.com/get") - if err != nil { - log.Fatal(err) - } - - // Read the whole body and close it. The span created by the - // othttp.Transport does not end until a read from the response - // body returns io.EOF or the response body is closed. - body, err := ioutil.ReadAll(response.Body) - response.Body.Close() - if err != nil { - log.Fatal(err) - } - - fmt.Printf("%s", body) - // body should look like this, with a different "traceparent" value: - // {"args":{},"headers":{"x-forwarded-proto":"https","host":"postman-echo.com","accept-encoding":"gzip","traceparent":"00-fb1d6775b94db561d9b51adbb3640de5-919c41073ec08f50-01","user-agent":"Go-http-client/1.1","x-forwarded-port":"443"},"url":"https://postman-echo.com/get"} } diff --git a/sdk/metric/benchmark_test.go b/sdk/metric/benchmark_test.go index 33d0f4953bd..204dbdfbca9 100644 --- a/sdk/metric/benchmark_test.go +++ b/sdk/metric/benchmark_test.go @@ -20,6 +20,7 @@ import ( "math/rand" "testing" + "go.opentelemetry.io/otel/api/global" "go.opentelemetry.io/otel/api/kv" "go.opentelemetry.io/otel/api/label" "go.opentelemetry.io/otel/api/metric" @@ -29,7 +30,7 @@ import ( ) type benchFixture struct { - meter metric.MeterMust + meter metric.Meter accumulator *sdk.Accumulator B *testing.B export.AggregatorSelector @@ -43,7 +44,7 @@ func newFixture(b *testing.B) *benchFixture { } bf.accumulator = sdk.NewAccumulator(bf) - bf.meter = metric.Must(metric.WrapMeterImpl(bf.accumulator, "benchmarks")) + bf.meter = metric.WrapMeterImpl(bf.accumulator, "benchmarks") return bf } @@ -51,6 +52,14 @@ func (f *benchFixture) Process(export.Accumulation) error { return nil } +func (f *benchFixture) Meter(_ string, _ ...metric.MeterOption) metric.Meter { + return f.meter +} + +func (f *benchFixture) meterMust() metric.MeterMust { + return metric.Must(f.meter) +} + func makeManyLabels(n int) [][]kv.KeyValue { r := make([][]kv.KeyValue, n) @@ -82,7 +91,7 @@ func benchmarkLabels(b *testing.B, n int) { ctx := context.Background() fix := newFixture(b) labs := makeLabels(n) - cnt := fix.meter.NewInt64Counter("int64.counter") + cnt := fix.meterMust().NewInt64Counter("int64.counter") b.ResetTimer() @@ -117,7 +126,7 @@ func BenchmarkInt64CounterAddWithLabels_16(b *testing.B) { func BenchmarkAcquireNewHandle(b *testing.B) { fix := newFixture(b) labelSets := makeManyLabels(b.N) - cnt := fix.meter.NewInt64Counter("int64.counter") + cnt := fix.meterMust().NewInt64Counter("int64.counter") b.ResetTimer() @@ -129,7 +138,7 @@ func BenchmarkAcquireNewHandle(b *testing.B) { func BenchmarkAcquireExistingHandle(b *testing.B) { fix := newFixture(b) labelSets := makeManyLabels(b.N) - cnt := fix.meter.NewInt64Counter("int64.counter") + cnt := fix.meterMust().NewInt64Counter("int64.counter") for i := 0; i < b.N; i++ { cnt.Bind(labelSets[i]...).Unbind() @@ -145,7 +154,7 @@ func BenchmarkAcquireExistingHandle(b *testing.B) { func BenchmarkAcquireReleaseExistingHandle(b *testing.B) { fix := newFixture(b) labelSets := makeManyLabels(b.N) - cnt := fix.meter.NewInt64Counter("int64.counter") + cnt := fix.meterMust().NewInt64Counter("int64.counter") for i := 0; i < b.N; i++ { cnt.Bind(labelSets[i]...).Unbind() @@ -199,11 +208,31 @@ func BenchmarkIterator_16(b *testing.B) { // Counters +func BenchmarkGlobalInt64CounterAddWithSDK(b *testing.B) { + // Compare with BenchmarkInt64CounterAdd() to see overhead of global + // package. This is in the SDK to avoid the API from depending on the + // SDK. + ctx := context.Background() + fix := newFixture(b) + + sdk := global.Meter("test") + global.SetMeterProvider(fix) + + labs := []kv.KeyValue{kv.String("A", "B")} + cnt := Must(sdk).NewInt64Counter("int64.counter") + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + cnt.Add(ctx, 1, labs...) + } +} + func BenchmarkInt64CounterAdd(b *testing.B) { ctx := context.Background() fix := newFixture(b) labs := makeLabels(1) - cnt := fix.meter.NewInt64Counter("int64.counter") + cnt := fix.meterMust().NewInt64Counter("int64.counter") b.ResetTimer() @@ -216,7 +245,7 @@ func BenchmarkInt64CounterHandleAdd(b *testing.B) { ctx := context.Background() fix := newFixture(b) labs := makeLabels(1) - cnt := fix.meter.NewInt64Counter("int64.counter") + cnt := fix.meterMust().NewInt64Counter("int64.counter") handle := cnt.Bind(labs...) b.ResetTimer() @@ -230,7 +259,7 @@ func BenchmarkFloat64CounterAdd(b *testing.B) { ctx := context.Background() fix := newFixture(b) labs := makeLabels(1) - cnt := fix.meter.NewFloat64Counter("float64.counter") + cnt := fix.meterMust().NewFloat64Counter("float64.counter") b.ResetTimer() @@ -243,7 +272,7 @@ func BenchmarkFloat64CounterHandleAdd(b *testing.B) { ctx := context.Background() fix := newFixture(b) labs := makeLabels(1) - cnt := fix.meter.NewFloat64Counter("float64.counter") + cnt := fix.meterMust().NewFloat64Counter("float64.counter") handle := cnt.Bind(labs...) b.ResetTimer() @@ -259,7 +288,7 @@ func BenchmarkInt64LastValueAdd(b *testing.B) { ctx := context.Background() fix := newFixture(b) labs := makeLabels(1) - mea := fix.meter.NewInt64ValueRecorder("int64.lastvalue") + mea := fix.meterMust().NewInt64ValueRecorder("int64.lastvalue") b.ResetTimer() @@ -272,7 +301,7 @@ func BenchmarkInt64LastValueHandleAdd(b *testing.B) { ctx := context.Background() fix := newFixture(b) labs := makeLabels(1) - mea := fix.meter.NewInt64ValueRecorder("int64.lastvalue") + mea := fix.meterMust().NewInt64ValueRecorder("int64.lastvalue") handle := mea.Bind(labs...) b.ResetTimer() @@ -286,7 +315,7 @@ func BenchmarkFloat64LastValueAdd(b *testing.B) { ctx := context.Background() fix := newFixture(b) labs := makeLabels(1) - mea := fix.meter.NewFloat64ValueRecorder("float64.lastvalue") + mea := fix.meterMust().NewFloat64ValueRecorder("float64.lastvalue") b.ResetTimer() @@ -299,7 +328,7 @@ func BenchmarkFloat64LastValueHandleAdd(b *testing.B) { ctx := context.Background() fix := newFixture(b) labs := makeLabels(1) - mea := fix.meter.NewFloat64ValueRecorder("float64.lastvalue") + mea := fix.meterMust().NewFloat64ValueRecorder("float64.lastvalue") handle := mea.Bind(labs...) b.ResetTimer() @@ -315,7 +344,7 @@ func benchmarkInt64ValueRecorderAdd(b *testing.B, name string) { ctx := context.Background() fix := newFixture(b) labs := makeLabels(1) - mea := fix.meter.NewInt64ValueRecorder(name) + mea := fix.meterMust().NewInt64ValueRecorder(name) b.ResetTimer() @@ -328,7 +357,7 @@ func benchmarkInt64ValueRecorderHandleAdd(b *testing.B, name string) { ctx := context.Background() fix := newFixture(b) labs := makeLabels(1) - mea := fix.meter.NewInt64ValueRecorder(name) + mea := fix.meterMust().NewInt64ValueRecorder(name) handle := mea.Bind(labs...) b.ResetTimer() @@ -342,7 +371,7 @@ func benchmarkFloat64ValueRecorderAdd(b *testing.B, name string) { ctx := context.Background() fix := newFixture(b) labs := makeLabels(1) - mea := fix.meter.NewFloat64ValueRecorder(name) + mea := fix.meterMust().NewFloat64ValueRecorder(name) b.ResetTimer() @@ -355,7 +384,7 @@ func benchmarkFloat64ValueRecorderHandleAdd(b *testing.B, name string) { ctx := context.Background() fix := newFixture(b) labs := makeLabels(1) - mea := fix.meter.NewFloat64ValueRecorder(name) + mea := fix.meterMust().NewFloat64ValueRecorder(name) handle := mea.Bind(labs...) b.ResetTimer() @@ -378,7 +407,7 @@ func BenchmarkObserverRegistration(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - fix.meter.NewInt64ValueObserver(names[i], cb) + fix.meterMust().NewInt64ValueObserver(names[i], cb) } } @@ -386,7 +415,7 @@ func BenchmarkValueObserverObservationInt64(b *testing.B) { ctx := context.Background() fix := newFixture(b) labs := makeLabels(1) - _ = fix.meter.NewInt64ValueObserver("test.valueobserver", func(_ context.Context, result metric.Int64ObserverResult) { + _ = fix.meterMust().NewInt64ValueObserver("test.valueobserver", func(_ context.Context, result metric.Int64ObserverResult) { for i := 0; i < b.N; i++ { result.Observe((int64)(i), labs...) } @@ -401,7 +430,7 @@ func BenchmarkValueObserverObservationFloat64(b *testing.B) { ctx := context.Background() fix := newFixture(b) labs := makeLabels(1) - _ = fix.meter.NewFloat64ValueObserver("test.valueobserver", func(_ context.Context, result metric.Float64ObserverResult) { + _ = fix.meterMust().NewFloat64ValueObserver("test.valueobserver", func(_ context.Context, result metric.Float64ObserverResult) { for i := 0; i < b.N; i++ { result.Observe((float64)(i), labs...) } @@ -476,7 +505,7 @@ func benchmarkBatchRecord8Labels(b *testing.B, numInst int) { var meas []metric.Measurement for i := 0; i < numInst; i++ { - inst := fix.meter.NewInt64Counter(fmt.Sprint("int64.counter.", i)) + inst := fix.meterMust().NewInt64Counter(fmt.Sprint("int64.counter.", i)) meas = append(meas, inst.Measurement(1)) } @@ -509,7 +538,7 @@ func BenchmarkRepeatedDirectCalls(b *testing.B) { ctx := context.Background() fix := newFixture(b) - c := fix.meter.NewInt64Counter("int64.counter") + c := fix.meterMust().NewInt64Counter("int64.counter") k := kv.String("bench", "true") b.ResetTimer()