From 11f62640ee673ca2afe078bbc95b8d5df20fccd5 Mon Sep 17 00:00:00 2001 From: Tyler Yahn Date: Thu, 29 Jul 2021 10:00:58 -0700 Subject: [PATCH] Add a SpanRecorder to the sdk/trace/tracetest (#2132) * Add a SpanRecorder to the sdk/trace/tracetest * Add changes to changelog * Run make precommit * Rename tests --- CHANGELOG.md | 2 + sdk/trace/tracetest/recorder.go | 67 ++++++++++++++++++++++ sdk/trace/tracetest/recorder_test.go | 85 ++++++++++++++++++++++++++++ 3 files changed, 154 insertions(+) create mode 100644 sdk/trace/tracetest/recorder.go create mode 100644 sdk/trace/tracetest/recorder_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index aee8a03a3..373e5abbf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Added a new `Link` type under the SDK `otel/sdk/trace` package that counts the number of attributes that were dropped for surpassing the `AttributePerLinkCountLimit` configured in the Span's `SpanLimits`. This new type replaces the equal-named API `Link` type found in the `otel/trace` package for most usages within the SDK. For example, instances of this type are now returned by the `Links()` function of `ReadOnlySpan`s provided in places like the `OnEnd` function of `SpanProcessor` implementations. (#2118) +- Added the `SpanRecorder` type to the `go.opentelemetry.io/otel/skd/trace/tracetest` package. + This type can be used with the default SDK as a `SpanProcessor` during testing. (#2132) ### Changed diff --git a/sdk/trace/tracetest/recorder.go b/sdk/trace/tracetest/recorder.go new file mode 100644 index 000000000..b0f647493 --- /dev/null +++ b/sdk/trace/tracetest/recorder.go @@ -0,0 +1,67 @@ +// 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/trace/tracetest" + +import ( + "context" + + sdktrace "go.opentelemetry.io/otel/sdk/trace" +) + +// SpanRecorder records started and ended spans. +type SpanRecorder struct { + started []sdktrace.ReadWriteSpan + ended []sdktrace.ReadOnlySpan +} + +var _ sdktrace.SpanProcessor = (*SpanRecorder)(nil) + +func NewSpanRecorder() *SpanRecorder { + return new(SpanRecorder) +} + +// OnStart records started spans. +func (sr *SpanRecorder) OnStart(_ context.Context, s sdktrace.ReadWriteSpan) { + sr.started = append(sr.started, s) +} + +// OnEnd records completed spans. +func (sr *SpanRecorder) OnEnd(s sdktrace.ReadOnlySpan) { + sr.ended = append(sr.ended, s) +} + +// Shutdown does nothing. +func (sr *SpanRecorder) Shutdown(context.Context) error { + return nil +} + +// ForceFlush does nothing. +func (sr *SpanRecorder) ForceFlush(context.Context) error { + return nil +} + +// Started returns a copy of all started spans that have been recorded. +func (sr *SpanRecorder) Started() []sdktrace.ReadWriteSpan { + dst := make([]sdktrace.ReadWriteSpan, len(sr.started)) + copy(dst, sr.started) + return dst +} + +// Ended returns a copy of all ended spans that have been recorded. +func (sr *SpanRecorder) Ended() []sdktrace.ReadOnlySpan { + dst := make([]sdktrace.ReadOnlySpan, len(sr.ended)) + copy(dst, sr.ended) + return dst +} diff --git a/sdk/trace/tracetest/recorder_test.go b/sdk/trace/tracetest/recorder_test.go new file mode 100644 index 000000000..46f9a57c9 --- /dev/null +++ b/sdk/trace/tracetest/recorder_test.go @@ -0,0 +1,85 @@ +// 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" + + sdktrace "go.opentelemetry.io/otel/sdk/trace" +) + +type rwSpan struct { + sdktrace.ReadWriteSpan +} + +func TestSpanRecorderOnStartAppends(t *testing.T) { + s0, s1 := new(rwSpan), new(rwSpan) + ctx := context.Background() + sr := new(SpanRecorder) + + assert.Len(t, sr.started, 0) + sr.OnStart(ctx, s0) + assert.Len(t, sr.started, 1) + sr.OnStart(ctx, s1) + assert.Len(t, sr.started, 2) + + // Ensure order correct. + started := sr.Started() + assert.Same(t, s0, started[0]) + assert.Same(t, s1, started[1]) +} + +type roSpan struct { + sdktrace.ReadOnlySpan +} + +func TestSpanRecorderOnEndAppends(t *testing.T) { + s0, s1 := new(roSpan), new(roSpan) + sr := new(SpanRecorder) + + assert.Len(t, sr.ended, 0) + sr.OnEnd(s0) + assert.Len(t, sr.ended, 1) + sr.OnEnd(s1) + assert.Len(t, sr.ended, 2) + + // Ensure order correct. + ended := sr.Ended() + assert.Same(t, s0, ended[0]) + assert.Same(t, s1, ended[1]) +} + +func TestSpanRecorderShutdownNoError(t *testing.T) { + ctx := context.Background() + assert.NoError(t, new(SpanRecorder).Shutdown(ctx)) + + var c context.CancelFunc + ctx, c = context.WithCancel(ctx) + c() + assert.NoError(t, new(SpanRecorder).Shutdown(ctx)) +} + +func TestSpanRecorderForceFlushNoError(t *testing.T) { + ctx := context.Background() + assert.NoError(t, new(SpanRecorder).ForceFlush(ctx)) + + var c context.CancelFunc + ctx, c = context.WithCancel(ctx) + c() + assert.NoError(t, new(SpanRecorder).ForceFlush(ctx)) +}