From 8be5adbe2bcbf71eb77d511855dcbfdea5ef79e0 Mon Sep 17 00:00:00 2001 From: Tyler Yahn Date: Sat, 25 Jul 2020 10:11:07 -0700 Subject: [PATCH 1/9] Remove SDK from othttp instrumentation --- .../othttp/transport_example_test.go | 34 +------------------ 1 file changed, 1 insertion(+), 33 deletions(-) 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"} } From 8e597d56984d399e4cff01f5d5e3ee12afdc8d1b Mon Sep 17 00:00:00 2001 From: Tyler Yahn Date: Sat, 25 Jul 2020 11:28:47 -0700 Subject: [PATCH 2/9] Remove dependency on SDK in api/kv pkg Benchmark the kv package not the SDK here. --- api/kv/benchmark_test.go | 351 ++++++++++++++++++++++++++++++++++++--- api/kv/key_test.go | 40 ----- 2 files changed, 325 insertions(+), 66 deletions(-) diff --git a/api/kv/benchmark_test.go b/api/kv/benchmark_test.go index a258e06ac04..af6048131ec 100644 --- a/api/kv/benchmark_test.go +++ b/api/kv/benchmark_test.go @@ -15,62 +15,361 @@ 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 BenchmarkArrayKeyInfer(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Infer("array", arrayVal) + } +} + +func BenchmarkBoolKey(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Bool("bool", boolVal) + } +} + +func BenchmarkBoolKeyInfer(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Infer("bool", boolVal) + } +} + +func BenchmarkIntKey(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Int("int", intVal) + } +} + +func BenchmarkIntKeyInfer(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Infer("int", intVal) + } +} + +func BenchmarkInt8KeyInfer(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Infer("int8", int8Val) + } +} + +func BenchmarkInt16KeyInfer(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Infer("int16", int16Val) + } +} + +func BenchmarkInt32Key(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Int32("int32", int32Val) + } +} + +func BenchmarkInt32KeyInfer(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Infer("int32", int32Val) + } +} + +func BenchmarkInt64Key(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Int64("int64", int64Val) + } +} + +func BenchmarkInt64KeyInfer(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Infer("int64", int64Val) + } +} + +func BenchmarkUintKey(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Uint("uint", uintVal) + } +} + +func BenchmarkUintKeyInfer(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Infer("uint", uintVal) + } +} + +func BenchmarkUint8KeyInfer(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Infer("uint8", uint8Val) + } +} + +func BenchmarkUint16KeyInfer(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Infer("uint16", uint16Val) + } +} + +func BenchmarkUint32Key(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Uint32("uint32", uint32Val) + } +} + +func BenchmarkUint32KeyInfer(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Infer("uint32", uint32Val) + } +} + +func BenchmarkUint64Key(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Uint64("uint64", uint64Val) + } +} + +func BenchmarkUint64KeyInfer(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Infer("uint64", uint64Val) + } +} + +func BenchmarkFloat32Key(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Float32("float32", float32Val) + } +} + +func BenchmarkFloat32KeyInfer(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Infer("float32", float32Val) + } +} + +func BenchmarkFloat64Key(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Float64("float64", float64Val) + } +} + +func BenchmarkFloat64KeyInfer(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Infer("float64", float64Val) + } +} + +func BenchmarkStringKey(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.String("string", stringVal) + } +} + +func BenchmarkStringKeyInfer(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Infer("string", stringVal) + } +} + +func BenchmarkBytesKeyInfer(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Infer("bytes", bytesVal) + // TODO struct json + } +} + +func BenchmarkStructKeyInfer(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = kv.Infer("struct", structVal) + } } -func BenchmarkKeyInfer(b *testing.B) { +func BenchmarkEmitArray(b *testing.B) { + b.ReportAllocs() for i := 0; i < b.N; i++ { - kv.Infer("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.Infer("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() - } -} From a44a742bc57532e30cde4323855a71987252fb21 Mon Sep 17 00:00:00 2001 From: Tyler Yahn Date: Sun, 26 Jul 2020 14:56:10 -0700 Subject: [PATCH 3/9] Update api/global benchmarks Move SDK related tests to SDK where applicable --- api/global/internal/benchmark_test.go | 101 +++----------------------- api/global/internal/meter_test.go | 2 + sdk/metric/benchmark_test.go | 75 +++++++++++++------ 3 files changed, 64 insertions(+), 114 deletions(-) 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/sdk/metric/benchmark_test.go b/sdk/metric/benchmark_test.go index 33d0f4953bd..fa6a3a1d784 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 (fix *benchFixture) Meter(_ string, _ ...metric.MeterOption) metric.Meter { + return fix.meter +} + +func (fix *benchFixture) meterMust() metric.MeterMust { + return metric.Must(fix.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() From 5e9d0e58e23c29741a418850e15d6df4d41f7474 Mon Sep 17 00:00:00 2001 From: Tyler Yahn Date: Sun, 26 Jul 2020 14:59:15 -0700 Subject: [PATCH 4/9] Add internal testing SDK implementation To be used by the API for testing so it does not depend on the actual SDK. --- internal/sdk/provider.go | 110 ++++++++++++++++++++++++++++++++ internal/sdk/span.go | 134 +++++++++++++++++++++++++++++++++++++++ internal/sdk/tracer.go | 61 ++++++++++++++++++ 3 files changed, 305 insertions(+) create mode 100644 internal/sdk/provider.go create mode 100644 internal/sdk/span.go create mode 100644 internal/sdk/tracer.go diff --git a/internal/sdk/provider.go b/internal/sdk/provider.go new file mode 100644 index 00000000000..9684507e65b --- /dev/null +++ b/internal/sdk/provider.go @@ -0,0 +1,110 @@ +// 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 sdk + +import ( + "context" + "encoding/binary" + "sync/atomic" + + "go.opentelemetry.io/otel/api/trace" +) + +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 TracingConfig 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 +} + +type TracingOption interface { + Apply(*TracingConfig) +} + +type spanContextFuncOption struct { + SpanContextFunc func(context.Context) trace.SpanContext +} + +func (o spanContextFuncOption) Apply(c *TracingConfig) { + c.SpanContextFunc = o.SpanContextFunc +} + +func WithSpanContextFunc(f func(context.Context) trace.SpanContext) TracingOption { + return spanContextFuncOption{f} +} + +type spanRecorderOption struct { + SpanRecorder SpanRecorder +} + +func (o spanRecorderOption) Apply(c *TracingConfig) { + c.SpanRecorder = o.SpanRecorder +} + +func WithSpanRecorder(sr SpanRecorder) TracingOption { + return spanRecorderOption{sr} +} + +type SpanRecorder interface { + OnStart(*Span) + OnEnd(*Span) +} + +type TraceProvider struct { + Config TracingConfig +} + +var _ trace.Provider = TraceProvider{} + +func NewTraceProvider(opts ...TracingOption) TraceProvider { + conf := TracingConfig{} + for _, opt := range opts { + opt.Apply(&conf) + } + if conf.SpanContextFunc == nil { + conf.SpanContextFunc = defaultSpanContextFunc() + } + return TraceProvider{Config: conf} +} + +func (p TraceProvider) Tracer(instName string, opts ...trace.TracerOption) trace.Tracer { + conf := new(trace.TracerConfig) + for _, o := range opts { + o(conf) + } + return &Tracer{ + Name: instName, + Version: conf.InstrumentationVersion, + Config: &p.Config, + } +} diff --git a/internal/sdk/span.go b/internal/sdk/span.go new file mode 100644 index 00000000000..1b719d5f565 --- /dev/null +++ b/internal/sdk/span.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 sdk + +import ( + "context" + "time" + + "go.opentelemetry.io/otel/api/kv" + "go.opentelemetry.io/otel/api/trace" + "google.golang.org/grpc/codes" +) + +// Event is a Span Event. +type Event struct { + // Name is the event name. + Name string + + // Timestamp is the time of recording. + Timestamp time.Time + + // Attributes are the identifying attributes. + Attributes []kv.KeyValue +} + +// Span is a mock span used in association with Tracer for testing purpose only. +type Span struct { + sc trace.SpanContext + tracer *Tracer + Name string + Status codes.Code + StatusMsg string + attributeMap map[kv.Key]int + Attributes []kv.KeyValue + Events []Event + Errors []error +} + +var _ trace.Span = (*Span)(nil) + +// SpanContext returns associated kv.SpanContext. If the receiver is nil it returns +// an empty kv.SpanContext +func (s *Span) SpanContext() trace.SpanContext { + if s == nil { + return trace.EmptySpanContext() + } + return s.sc +} + +// IsRecording always returns false for Span. +func (s *Span) IsRecording() bool { + return false +} + +// SetStatus sets the span Status and StatusMsg. +func (s *Span) SetStatus(status codes.Code, msg string) { + s.Status = status + s.StatusMsg = msg +} + +// SetError does nothing. +func (s *Span) SetError(v bool) { +} + +// SetAttributes sets attributes. +func (s *Span) SetAttributes(attrs ...kv.KeyValue) { + for _, attr := range attrs { + if attr.Value.Type() == kv.INVALID { + continue + } + if idx, ok := s.attributeMap[attr.Key]; !ok { + s.Attributes = append(s.Attributes, attr) + s.attributeMap[attr.Key] = len(s.Attributes) - 1 + } else { + s.Attributes[idx] = attr + } + } +} + +// SetAttribute sets attribute k to value v. +func (s *Span) SetAttribute(k string, v interface{}) { + attr := kv.Infer(k, v) + if attr.Value.Type() != kv.INVALID { + s.SetAttributes(attr) + } +} + +// End ends the span by calling the configured SpanRecorder.OnEnd. +func (s *Span) End(options ...trace.EndOption) { + if s.tracer.Config.SpanRecorder != nil { + s.tracer.Config.SpanRecorder.OnEnd(s) + } +} + +// RecordError records err +func (s *Span) RecordError(_ context.Context, err error, _ ...trace.ErrorOption) { + s.Errors = append(s.Errors, err) +} + +// SetName sets the span name. +func (s *Span) SetName(name string) { + s.Name = name +} + +// Tracer returns Tracer that created this Span. +func (s *Span) Tracer() trace.Tracer { + return s.tracer +} + +// AddEvent records an event. +func (s *Span) AddEvent(ctx context.Context, name string, attrs ...kv.KeyValue) { + s.AddEventWithTimestamp(ctx, time.Now(), name, attrs...) +} + +// AddEvent records an event occuring at timestamp. +func (s *Span) AddEventWithTimestamp(_ context.Context, timestamp time.Time, name string, attrs ...kv.KeyValue) { + s.Events = append(s.Events, Event{ + Name: name, + Timestamp: timestamp, + Attributes: attrs, + }) +} diff --git a/internal/sdk/tracer.go b/internal/sdk/tracer.go new file mode 100644 index 00000000000..4c11cd9cdd9 --- /dev/null +++ b/internal/sdk/tracer.go @@ -0,0 +1,61 @@ +// 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 sdk + +import ( + "context" + + "go.opentelemetry.io/otel/api/trace" +) + +// Tracer is a simple tracer used for testing purpose only. +// It only supports ChildOf option. SpanId is atomically increased every time a +// new span is created. +type Tracer struct { + // Name is the instrumentation name. + Name string + // Version is the instrumentation version. + Version string + + Config *TracingConfig +} + +var _ trace.Tracer = (*Tracer)(nil) + +// WithSpan does nothing except executing the body. +func (t *Tracer) WithSpan(ctx context.Context, name string, body func(context.Context) error, opts ...trace.StartOption) error { + ctx, span := t.Start(ctx, name, opts...) + defer span.End() + + return body(ctx) +} + +// Start starts a Span. +func (t *Tracer) Start(ctx context.Context, name string, opts ...trace.StartOption) (context.Context, trace.Span) { + var conf trace.StartConfig + for _, opt := range opts { + opt(&conf) + } + span := &Span{ + sc: t.Config.SpanContextFunc(ctx), + tracer: t, + Name: name, + Attributes: conf.Attributes, + } + if t.Config.SpanRecorder != nil { + t.Config.SpanRecorder.OnStart(span) + } + return trace.ContextWithSpan(ctx, span), span +} From e391f660dbeb883723438c9b9d06ad7de34ad583 Mon Sep 17 00:00:00 2001 From: Tyler Yahn Date: Sun, 26 Jul 2020 15:00:02 -0700 Subject: [PATCH 5/9] Update api/global/internal to use internal/sdk --- api/global/internal/trace_test.go | 36 +++++++++++-------------------- 1 file changed, 13 insertions(+), 23 deletions(-) diff --git a/api/global/internal/trace_test.go b/api/global/internal/trace_test.go index ac6ceb43b69..491d527686f 100644 --- a/api/global/internal/trace_test.go +++ b/api/global/internal/trace_test.go @@ -22,28 +22,23 @@ import ( "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/internal/sdk" ) -type testSpanProcesor struct { - // Names of Spans started. - spansStarted []string - // Names of Spans ended. - spansEnded []string +type SpanRecorder struct { + Started []string + Ended []string } -func (t *testSpanProcesor) OnStart(s *export.SpanData) { - t.spansStarted = append(t.spansStarted, s.Name) +func (sr *SpanRecorder) OnStart(s *sdk.Span) { + sr.Started = append(sr.Started, s.Name) } -func (t *testSpanProcesor) OnEnd(s *export.SpanData) { - t.spansEnded = append(t.spansEnded, s.Name) +func (sr *SpanRecorder) OnEnd(s *sdk.Span) { + sr.Ended = append(sr.Ended, s.Name) } -func (t *testSpanProcesor) Shutdown() {} - -func TestTraceDefaultSDK(t *testing.T) { +func TestTraceWithSDK(t *testing.T) { internal.ResetForTest() ctx := context.Background() @@ -56,13 +51,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 := SpanRecorder{} + tp := sdk.NewTraceProvider(sdk.WithSpanRecorder(&sr)) global.SetTraceProvider(tp) // This span was started before initialization, it is expected to be dropped. @@ -84,6 +74,6 @@ func TestTraceDefaultSDK(t *testing.T) { } expected := []string{"span2", "withSpan2", "span3", "withSpan3"} - require.Equal(t, tsp.spansStarted, expected) - require.Equal(t, tsp.spansEnded, expected) + require.Equal(t, sr.Started, expected) + require.Equal(t, sr.Ended, expected) } From a41622c19e003b6898f001b8d573eee6d38b2e6f Mon Sep 17 00:00:00 2001 From: Tyler Yahn Date: Sun, 26 Jul 2020 15:05:46 -0700 Subject: [PATCH 6/9] Fix lint on sdk/metric benchmark --- sdk/metric/benchmark_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sdk/metric/benchmark_test.go b/sdk/metric/benchmark_test.go index fa6a3a1d784..204dbdfbca9 100644 --- a/sdk/metric/benchmark_test.go +++ b/sdk/metric/benchmark_test.go @@ -52,12 +52,12 @@ func (f *benchFixture) Process(export.Accumulation) error { return nil } -func (fix *benchFixture) Meter(_ string, _ ...metric.MeterOption) metric.Meter { - return fix.meter +func (f *benchFixture) Meter(_ string, _ ...metric.MeterOption) metric.Meter { + return f.meter } -func (fix *benchFixture) meterMust() metric.MeterMust { - return metric.Must(fix.meter) +func (f *benchFixture) meterMust() metric.MeterMust { + return metric.Must(f.meter) } func makeManyLabels(n int) [][]kv.KeyValue { From 32689ac0c512daa8a8b7650756c3da30d59c72d5 Mon Sep 17 00:00:00 2001 From: Tyler Yahn Date: Sun, 26 Jul 2020 15:06:17 -0700 Subject: [PATCH 7/9] Lint internal/sdk --- internal/sdk/span.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/internal/sdk/span.go b/internal/sdk/span.go index 1b719d5f565..71af62baced 100644 --- a/internal/sdk/span.go +++ b/internal/sdk/span.go @@ -18,9 +18,10 @@ import ( "context" "time" + "google.golang.org/grpc/codes" + "go.opentelemetry.io/otel/api/kv" "go.opentelemetry.io/otel/api/trace" - "google.golang.org/grpc/codes" ) // Event is a Span Event. @@ -124,7 +125,7 @@ func (s *Span) AddEvent(ctx context.Context, name string, attrs ...kv.KeyValue) s.AddEventWithTimestamp(ctx, time.Now(), name, attrs...) } -// AddEvent records an event occuring at timestamp. +// AddEvent records an event occurring at timestamp. func (s *Span) AddEventWithTimestamp(_ context.Context, timestamp time.Time, name string, attrs ...kv.KeyValue) { s.Events = append(s.Events, Event{ Name: name, From 7510746f1c6e53811c19d54e58f7e5f9430d1511 Mon Sep 17 00:00:00 2001 From: Tyler Yahn Date: Mon, 27 Jul 2020 16:28:43 -0700 Subject: [PATCH 8/9] Merge internal/sdk into api/trace/testtrace --- api/global/internal/trace_test.go | 32 ++--- .../trace/testtrace/config.go | 80 +++++++---- api/trace/testtrace/generator.go | 73 ---------- api/trace/testtrace/provider.go | 64 +++++++++ api/trace/testtrace/span.go | 6 +- api/trace/testtrace/span_test.go | 58 ++++---- api/trace/testtrace/tracer.go | 125 +++++----------- api/trace/testtrace/tracer_test.go | 51 ++++--- internal/sdk/span.go | 135 ------------------ internal/sdk/tracer.go | 61 -------- 10 files changed, 234 insertions(+), 451 deletions(-) rename internal/sdk/provider.go => api/trace/testtrace/config.go (62%) delete mode 100644 api/trace/testtrace/generator.go create mode 100644 api/trace/testtrace/provider.go delete mode 100644 internal/sdk/span.go delete mode 100644 internal/sdk/tracer.go diff --git a/api/global/internal/trace_test.go b/api/global/internal/trace_test.go index 491d527686f..58d73aa5eb0 100644 --- a/api/global/internal/trace_test.go +++ b/api/global/internal/trace_test.go @@ -18,26 +18,13 @@ 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" - "go.opentelemetry.io/otel/internal/sdk" + "go.opentelemetry.io/otel/api/trace/testtrace" ) -type SpanRecorder struct { - Started []string - Ended []string -} - -func (sr *SpanRecorder) OnStart(s *sdk.Span) { - sr.Started = append(sr.Started, s.Name) -} - -func (sr *SpanRecorder) OnEnd(s *sdk.Span) { - sr.Ended = append(sr.Ended, s.Name) -} - func TestTraceWithSDK(t *testing.T) { internal.ResetForTest() @@ -51,8 +38,8 @@ func TestTraceWithSDK(t *testing.T) { t.Errorf("failed to wrap function with span prior to initialization: %v", err) } - sr := SpanRecorder{} - tp := sdk.NewTraceProvider(sdk.WithSpanRecorder(&sr)) + 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. @@ -73,7 +60,14 @@ func TestTraceWithSDK(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, sr.Started, expected) - require.Equal(t, sr.Ended, expected) + assert.ElementsMatch(t, expected, filterNames(sr.Started())) + assert.ElementsMatch(t, expected, filterNames(sr.Completed())) } diff --git a/internal/sdk/provider.go b/api/trace/testtrace/config.go similarity index 62% rename from internal/sdk/provider.go rename to api/trace/testtrace/config.go index 9684507e65b..b4557676c18 100644 --- a/internal/sdk/provider.go +++ b/api/trace/testtrace/config.go @@ -12,16 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. -package sdk +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 { @@ -38,7 +40,7 @@ func defaultSpanContextFunc() func(context.Context) trace.SpanContext { } } -type TracingConfig struct { +type config struct { // SpanContextFunc returns a SpanContext from an parent Context for a // new span. SpanContextFunc func(context.Context) trace.SpanContext @@ -47,19 +49,30 @@ type TracingConfig struct { SpanRecorder SpanRecorder } -type TracingOption interface { - Apply(*TracingConfig) +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 *TracingConfig) { +func (o spanContextFuncOption) Apply(c *config) { c.SpanContextFunc = o.SpanContextFunc } -func WithSpanContextFunc(f func(context.Context) trace.SpanContext) TracingOption { +func WithSpanContextFunc(f func(context.Context) trace.SpanContext) Option { return spanContextFuncOption{f} } @@ -67,11 +80,11 @@ type spanRecorderOption struct { SpanRecorder SpanRecorder } -func (o spanRecorderOption) Apply(c *TracingConfig) { +func (o spanRecorderOption) Apply(c *config) { c.SpanRecorder = o.SpanRecorder } -func WithSpanRecorder(sr SpanRecorder) TracingOption { +func WithSpanRecorder(sr SpanRecorder) Option { return spanRecorderOption{sr} } @@ -80,31 +93,42 @@ type SpanRecorder interface { OnEnd(*Span) } -type TraceProvider struct { - Config TracingConfig +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) } -var _ trace.Provider = TraceProvider{} +func (ssr *StandardSpanRecorder) OnEnd(span *Span) { + ssr.doneMu.Lock() + defer ssr.doneMu.Unlock() + ssr.done = append(ssr.done, span) +} -func NewTraceProvider(opts ...TracingOption) TraceProvider { - conf := TracingConfig{} - for _, opt := range opts { - opt.Apply(&conf) +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] } - if conf.SpanContextFunc == nil { - conf.SpanContextFunc = defaultSpanContextFunc() - } - return TraceProvider{Config: conf} + return started } -func (p TraceProvider) Tracer(instName string, opts ...trace.TracerOption) trace.Tracer { - conf := new(trace.TracerConfig) - for _, o := range opts { - o(conf) - } - return &Tracer{ - Name: instName, - Version: conf.InstrumentationVersion, - Config: &p.Config, +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 66f6e005efa..c9b0ac1c85d 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/internal/sdk/span.go b/internal/sdk/span.go deleted file mode 100644 index 71af62baced..00000000000 --- a/internal/sdk/span.go +++ /dev/null @@ -1,135 +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 sdk - -import ( - "context" - "time" - - "google.golang.org/grpc/codes" - - "go.opentelemetry.io/otel/api/kv" - "go.opentelemetry.io/otel/api/trace" -) - -// Event is a Span Event. -type Event struct { - // Name is the event name. - Name string - - // Timestamp is the time of recording. - Timestamp time.Time - - // Attributes are the identifying attributes. - Attributes []kv.KeyValue -} - -// Span is a mock span used in association with Tracer for testing purpose only. -type Span struct { - sc trace.SpanContext - tracer *Tracer - Name string - Status codes.Code - StatusMsg string - attributeMap map[kv.Key]int - Attributes []kv.KeyValue - Events []Event - Errors []error -} - -var _ trace.Span = (*Span)(nil) - -// SpanContext returns associated kv.SpanContext. If the receiver is nil it returns -// an empty kv.SpanContext -func (s *Span) SpanContext() trace.SpanContext { - if s == nil { - return trace.EmptySpanContext() - } - return s.sc -} - -// IsRecording always returns false for Span. -func (s *Span) IsRecording() bool { - return false -} - -// SetStatus sets the span Status and StatusMsg. -func (s *Span) SetStatus(status codes.Code, msg string) { - s.Status = status - s.StatusMsg = msg -} - -// SetError does nothing. -func (s *Span) SetError(v bool) { -} - -// SetAttributes sets attributes. -func (s *Span) SetAttributes(attrs ...kv.KeyValue) { - for _, attr := range attrs { - if attr.Value.Type() == kv.INVALID { - continue - } - if idx, ok := s.attributeMap[attr.Key]; !ok { - s.Attributes = append(s.Attributes, attr) - s.attributeMap[attr.Key] = len(s.Attributes) - 1 - } else { - s.Attributes[idx] = attr - } - } -} - -// SetAttribute sets attribute k to value v. -func (s *Span) SetAttribute(k string, v interface{}) { - attr := kv.Infer(k, v) - if attr.Value.Type() != kv.INVALID { - s.SetAttributes(attr) - } -} - -// End ends the span by calling the configured SpanRecorder.OnEnd. -func (s *Span) End(options ...trace.EndOption) { - if s.tracer.Config.SpanRecorder != nil { - s.tracer.Config.SpanRecorder.OnEnd(s) - } -} - -// RecordError records err -func (s *Span) RecordError(_ context.Context, err error, _ ...trace.ErrorOption) { - s.Errors = append(s.Errors, err) -} - -// SetName sets the span name. -func (s *Span) SetName(name string) { - s.Name = name -} - -// Tracer returns Tracer that created this Span. -func (s *Span) Tracer() trace.Tracer { - return s.tracer -} - -// AddEvent records an event. -func (s *Span) AddEvent(ctx context.Context, name string, attrs ...kv.KeyValue) { - s.AddEventWithTimestamp(ctx, time.Now(), name, attrs...) -} - -// AddEvent records an event occurring at timestamp. -func (s *Span) AddEventWithTimestamp(_ context.Context, timestamp time.Time, name string, attrs ...kv.KeyValue) { - s.Events = append(s.Events, Event{ - Name: name, - Timestamp: timestamp, - Attributes: attrs, - }) -} diff --git a/internal/sdk/tracer.go b/internal/sdk/tracer.go deleted file mode 100644 index 4c11cd9cdd9..00000000000 --- a/internal/sdk/tracer.go +++ /dev/null @@ -1,61 +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 sdk - -import ( - "context" - - "go.opentelemetry.io/otel/api/trace" -) - -// Tracer is a simple tracer used for testing purpose only. -// It only supports ChildOf option. SpanId is atomically increased every time a -// new span is created. -type Tracer struct { - // Name is the instrumentation name. - Name string - // Version is the instrumentation version. - Version string - - Config *TracingConfig -} - -var _ trace.Tracer = (*Tracer)(nil) - -// WithSpan does nothing except executing the body. -func (t *Tracer) WithSpan(ctx context.Context, name string, body func(context.Context) error, opts ...trace.StartOption) error { - ctx, span := t.Start(ctx, name, opts...) - defer span.End() - - return body(ctx) -} - -// Start starts a Span. -func (t *Tracer) Start(ctx context.Context, name string, opts ...trace.StartOption) (context.Context, trace.Span) { - var conf trace.StartConfig - for _, opt := range opts { - opt(&conf) - } - span := &Span{ - sc: t.Config.SpanContextFunc(ctx), - tracer: t, - Name: name, - Attributes: conf.Attributes, - } - if t.Config.SpanRecorder != nil { - t.Config.SpanRecorder.OnStart(span) - } - return trace.ContextWithSpan(ctx, span), span -} From b6a8819da5de3066f55403325e97a0accd1f2d7c Mon Sep 17 00:00:00 2001 From: Tyler Yahn Date: Mon, 27 Jul 2020 16:44:09 -0700 Subject: [PATCH 9/9] Update Changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) 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