From 538f31e7476207147bf05bb68ff45e7377558089 Mon Sep 17 00:00:00 2001 From: Bogdan Drutu Date: Thu, 30 Jul 2020 09:20:49 -0700 Subject: [PATCH 1/3] Add Noop and InMemory SpanBatcher, help with testing integrations Signed-off-by: Bogdan Drutu --- sdk/export/trace/tracetest/test.go | 66 +++++++++++++++++++++++++ sdk/export/trace/tracetest/test_test.go | 57 +++++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 sdk/export/trace/tracetest/test.go create mode 100644 sdk/export/trace/tracetest/test_test.go diff --git a/sdk/export/trace/tracetest/test.go b/sdk/export/trace/tracetest/test.go new file mode 100644 index 00000000000..15764e72171 --- /dev/null +++ b/sdk/export/trace/tracetest/test.go @@ -0,0 +1,66 @@ +// 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 tracetest // import "go.opentelemetry.io/otel/sdk/export/trace" + +import ( + "context" + "sync" + + "go.opentelemetry.io/otel/sdk/export/trace" +) + +// NewNoopSpanBatcher returns a new no-op trace.SpanBatcher. +func NewNoopSpanBatcher() trace.SpanBatcher { + return new(noopSpanBatcher) +} + +type noopSpanBatcher struct { +} + +// ExportSpans implements the trace.SpanBatcher interface. +func (nsb *noopSpanBatcher) ExportSpans(context.Context, []*trace.SpanData) {} + +// NewInMemorySpanBatcher returns a new trace.SpanBatcher that stores in-memory all exported spans. +func NewInMemorySpanBatcher() *InMemorySpanBatcher { + return new(InMemorySpanBatcher) +} + +type InMemorySpanBatcher struct { + mu sync.Mutex + sds []*trace.SpanData +} + +// ExportSpans implements the trace.SpanBatcher interface. +func (imsb *InMemorySpanBatcher) ExportSpans(_ context.Context, sds []*trace.SpanData) { + imsb.mu.Lock() + defer imsb.mu.Unlock() + imsb.sds = append(imsb.sds, sds...) +} + +// Reset the current in-memory storage. +func (imsb *InMemorySpanBatcher) Reset() { + imsb.mu.Lock() + defer imsb.mu.Unlock() + imsb.sds = nil +} + +// GetSpans returns the current in-memory stored spans. +func (imsb *InMemorySpanBatcher) GetSpans() []*trace.SpanData { + imsb.mu.Lock() + defer imsb.mu.Unlock() + ret := make([]*trace.SpanData, len(imsb.sds)) + copy(ret, imsb.sds) + return ret +} diff --git a/sdk/export/trace/tracetest/test_test.go b/sdk/export/trace/tracetest/test_test.go new file mode 100644 index 00000000000..b6d691cfd14 --- /dev/null +++ b/sdk/export/trace/tracetest/test_test.go @@ -0,0 +1,57 @@ +// 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 tracetest + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + + "go.opentelemetry.io/otel/sdk/export/trace" +) + +var _ trace.SpanBatcher = (*InMemorySpanBatcher)(nil) + +// TestNoop tests only that the no-op does not crash in different scenarios. +func TestNoop(t *testing.T) { + nsb := NewNoopSpanBatcher() + + nsb.ExportSpans(context.Background(), nil) + nsb.ExportSpans(context.Background(), make([]*trace.SpanData, 10)) + nsb.ExportSpans(context.Background(), make([]*trace.SpanData, 0, 10)) +} + +func TestSink(t *testing.T) { + imsb := NewInMemorySpanBatcher() + + imsb.ExportSpans(context.Background(), nil) + assert.Len(t, imsb.GetSpans(), 0) + + input := make([]*trace.SpanData, 10) + for i := 0; i < 10; i++ { + input[i] = new(trace.SpanData) + } + imsb.ExportSpans(context.Background(), input) + sds := imsb.GetSpans() + assert.Len(t, sds, 10) + for i, sd := range sds { + assert.Same(t, input[i], sd) + } + imsb.Reset() + // Ensure that operations on the internal storage does not change the previously returned value. + assert.Len(t, sds, 10) + assert.Len(t, imsb.GetSpans(), 0) +} From a68f17115eadcfdd9e2b3c34dd8ab51b3766bdd7 Mon Sep 17 00:00:00 2001 From: Bogdan Drutu Date: Thu, 30 Jul 2020 12:51:39 -0700 Subject: [PATCH 2/3] Update sdk/export/trace/tracetest/test.go Co-authored-by: Tyler Yahn --- sdk/export/trace/tracetest/test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sdk/export/trace/tracetest/test.go b/sdk/export/trace/tracetest/test.go index 15764e72171..99e52cc0d5f 100644 --- a/sdk/export/trace/tracetest/test.go +++ b/sdk/export/trace/tracetest/test.go @@ -32,6 +32,9 @@ type noopSpanBatcher struct { // ExportSpans implements the trace.SpanBatcher interface. func (nsb *noopSpanBatcher) ExportSpans(context.Context, []*trace.SpanData) {} +// ExportSpan implements the trace.SpanSyncer interface. +func (nsb *noopSpanBatcher) ExportSpan(context.Context, trace.SpanData) {} + // NewInMemorySpanBatcher returns a new trace.SpanBatcher that stores in-memory all exported spans. func NewInMemorySpanBatcher() *InMemorySpanBatcher { return new(InMemorySpanBatcher) From 6b88e192bd1c94593490f976fce5ed211caafbc3 Mon Sep 17 00:00:00 2001 From: Bogdan Drutu Date: Thu, 30 Jul 2020 13:05:14 -0700 Subject: [PATCH 3/3] More feedback Signed-off-by: Bogdan Drutu --- sdk/export/trace/tracetest/test.go | 48 +++++++++++++++++-------- sdk/export/trace/tracetest/test_test.go | 14 +++++--- 2 files changed, 42 insertions(+), 20 deletions(-) diff --git a/sdk/export/trace/tracetest/test.go b/sdk/export/trace/tracetest/test.go index 99e52cc0d5f..a387df587d3 100644 --- a/sdk/export/trace/tracetest/test.go +++ b/sdk/export/trace/tracetest/test.go @@ -12,7 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -package tracetest // import "go.opentelemetry.io/otel/sdk/export/trace" +// tracetest is a testing helper package for the SDK. User can configure no-op or in-memory exporters to verify +// different SDK behaviors or custom instrumentation. +package tracetest // import "go.opentelemetry.io/otel/sdk/export/trace/tracetest" import ( "context" @@ -21,46 +23,62 @@ import ( "go.opentelemetry.io/otel/sdk/export/trace" ) -// NewNoopSpanBatcher returns a new no-op trace.SpanBatcher. -func NewNoopSpanBatcher() trace.SpanBatcher { - return new(noopSpanBatcher) -} +var _ trace.SpanBatcher = (*NoopExporter)(nil) +var _ trace.SpanSyncer = (*NoopExporter)(nil) -type noopSpanBatcher struct { +// NewNoopExporter returns a new no-op exporter. +// It implements both trace.SpanBatcher and trace.SpanSyncer. +func NewNoopExporter() *NoopExporter { + return new(NoopExporter) } +// NoopExporter is an exporter that does nothing. +type NoopExporter struct{} + // ExportSpans implements the trace.SpanBatcher interface. -func (nsb *noopSpanBatcher) ExportSpans(context.Context, []*trace.SpanData) {} +func (nsb *NoopExporter) ExportSpans(context.Context, []*trace.SpanData) {} // ExportSpan implements the trace.SpanSyncer interface. -func (nsb *noopSpanBatcher) ExportSpan(context.Context, trace.SpanData) {} +func (nsb *NoopExporter) ExportSpan(context.Context, *trace.SpanData) {} -// NewInMemorySpanBatcher returns a new trace.SpanBatcher that stores in-memory all exported spans. -func NewInMemorySpanBatcher() *InMemorySpanBatcher { - return new(InMemorySpanBatcher) +var _ trace.SpanBatcher = (*InMemoryExporter)(nil) +var _ trace.SpanSyncer = (*InMemoryExporter)(nil) + +// NewInMemoryExporter returns a new trace.SpanBatcher that stores in-memory all exported spans. +// It implements both trace.SpanBatcher and trace.SpanSyncer. +func NewInMemoryExporter() *InMemoryExporter { + return new(InMemoryExporter) } -type InMemorySpanBatcher struct { +// InMemoryExporter is an exporter that stores in-memory all exported spans. +type InMemoryExporter struct { mu sync.Mutex sds []*trace.SpanData } // ExportSpans implements the trace.SpanBatcher interface. -func (imsb *InMemorySpanBatcher) ExportSpans(_ context.Context, sds []*trace.SpanData) { +func (imsb *InMemoryExporter) ExportSpans(_ context.Context, sds []*trace.SpanData) { imsb.mu.Lock() defer imsb.mu.Unlock() imsb.sds = append(imsb.sds, sds...) } +// ExportSpan implements the trace.SpanSyncer interface. +func (imsb *InMemoryExporter) ExportSpan(_ context.Context, sd *trace.SpanData) { + imsb.mu.Lock() + defer imsb.mu.Unlock() + imsb.sds = append(imsb.sds, sd) +} + // Reset the current in-memory storage. -func (imsb *InMemorySpanBatcher) Reset() { +func (imsb *InMemoryExporter) Reset() { imsb.mu.Lock() defer imsb.mu.Unlock() imsb.sds = nil } // GetSpans returns the current in-memory stored spans. -func (imsb *InMemorySpanBatcher) GetSpans() []*trace.SpanData { +func (imsb *InMemoryExporter) GetSpans() []*trace.SpanData { imsb.mu.Lock() defer imsb.mu.Unlock() ret := make([]*trace.SpanData, len(imsb.sds)) diff --git a/sdk/export/trace/tracetest/test_test.go b/sdk/export/trace/tracetest/test_test.go index b6d691cfd14..46a385e33d7 100644 --- a/sdk/export/trace/tracetest/test_test.go +++ b/sdk/export/trace/tracetest/test_test.go @@ -23,19 +23,18 @@ import ( "go.opentelemetry.io/otel/sdk/export/trace" ) -var _ trace.SpanBatcher = (*InMemorySpanBatcher)(nil) - // TestNoop tests only that the no-op does not crash in different scenarios. func TestNoop(t *testing.T) { - nsb := NewNoopSpanBatcher() + nsb := NewNoopExporter() nsb.ExportSpans(context.Background(), nil) nsb.ExportSpans(context.Background(), make([]*trace.SpanData, 10)) nsb.ExportSpans(context.Background(), make([]*trace.SpanData, 0, 10)) + nsb.ExportSpan(context.Background(), nil) } -func TestSink(t *testing.T) { - imsb := NewInMemorySpanBatcher() +func TestNewInMemoryExporter(t *testing.T) { + imsb := NewInMemoryExporter() imsb.ExportSpans(context.Background(), nil) assert.Len(t, imsb.GetSpans(), 0) @@ -54,4 +53,9 @@ func TestSink(t *testing.T) { // Ensure that operations on the internal storage does not change the previously returned value. assert.Len(t, sds, 10) assert.Len(t, imsb.GetSpans(), 0) + + imsb.ExportSpan(context.Background(), input[0]) + sds = imsb.GetSpans() + assert.Len(t, sds, 1) + assert.Same(t, input[0], sds[0]) }