You've already forked opentelemetry-go
mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2025-07-17 01:12:45 +02:00
Add embedded package to trace API (#4620)
* Add trace/embedded * Update trace impl to use trace/embedded * Add noop pkg to replace no-op impl in trace pkg * Use trace/embedded in global impl * Use trace/embedded in SDK impl * Update opencensus bridge * Update opentracing bridge * Add changes to changelog * Update trace/doc.go Co-authored-by: David Ashpole <dashpole@google.com> --------- Co-authored-by: David Ashpole <dashpole@google.com>
This commit is contained in:
16
CHANGELOG.md
16
CHANGELOG.md
@ -12,16 +12,32 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
||||
|
||||
- Add `go.opentelemetry.io/otel/bridge/opencensus.InstallTraceBridge`, which installs the OpenCensus trace bridge, and replaces `opencensus.NewTracer`. (#4567)
|
||||
- Add scope version to trace and metric bridges in `go.opentelemetry.io/otel/bridge/opencensus`. (#4584)
|
||||
- Add the `go.opentelemetry.io/otel/trace/embedded` package to be embedded in the exported trace API interfaces. (#4620)
|
||||
- Add the `go.opentelemetry.io/otel/trace/noop` package as a default no-op implementation of the trace API. (#4620)
|
||||
- Add context propagation in `go.opentelemetry.io/otel/example/dice`. (#4644)
|
||||
|
||||
### Deprecated
|
||||
|
||||
- Deprecate `go.opentelemetry.io/otel/bridge/opencensus.NewTracer` in favor of `opencensus.InstallTraceBridge`. (#4567)
|
||||
- Deprecate `go.opentelemetry.io/otel/example/fib` package is in favor of `go.opentelemetry.io/otel/example/dice`. (#4618)
|
||||
- Deprecate `go.opentelemetry.io/otel/trace.NewNoopTracerProvider`.
|
||||
Use the added `NewTracerProvider` function in `go.opentelemetry.io/otel/trace/noop` instead. (#4620)
|
||||
|
||||
### Changed
|
||||
|
||||
- `go.opentelemetry.io/otel/bridge/opencensus.NewMetricProducer` returns a `*MetricProducer` struct instead of the metric.Producer interface. (#4583)
|
||||
- The `TracerProvider` in `go.opentelemetry.io/otel/trace` now embeds the `go.opentelemetry.io/otel/trace/embedded.TracerProvider` type.
|
||||
This extends the `TracerProvider` interface and is is a breaking change for any existing implementation.
|
||||
Implementors need to update their implementations based on what they want the default behavior of the interface to be.
|
||||
See the "API Implementations" section of the `go.opentelemetry.io/otel/trace` package documentation for more informatoin about how to accomplish this. (#4620)
|
||||
- The `Tracer` in `go.opentelemetry.io/otel/trace` now embeds the `go.opentelemetry.io/otel/trace/embedded.Tracer` type.
|
||||
This extends the `Tracer` interface and is is a breaking change for any existing implementation.
|
||||
Implementors need to update their implementations based on what they want the default behavior of the interface to be.
|
||||
See the "API Implementations" section of the `go.opentelemetry.io/otel/trace` package documentation for more informatoin about how to accomplish this. (#4620)
|
||||
- The `Span` in `go.opentelemetry.io/otel/trace` now embeds the `go.opentelemetry.io/otel/trace/embedded.Span` type.
|
||||
This extends the `Span` interface and is is a breaking change for any existing implementation.
|
||||
Implementors need to update their implementations based on what they want the default behavior of the interface to be.
|
||||
See the "API Implementations" section of the `go.opentelemetry.io/otel/trace` package documentation for more informatoin about how to accomplish this. (#4620)
|
||||
|
||||
## [1.19.0/0.42.0/0.0.7] 2023-09-28
|
||||
|
||||
|
@ -20,12 +20,12 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"go.opentelemetry.io/otel/trace/noop"
|
||||
)
|
||||
|
||||
func TestNewTraceConfig(t *testing.T) {
|
||||
globalTP := trace.NewNoopTracerProvider()
|
||||
customTP := trace.NewNoopTracerProvider()
|
||||
globalTP := noop.NewTracerProvider()
|
||||
customTP := noop.NewTracerProvider()
|
||||
otel.SetTracerProvider(globalTP)
|
||||
for _, tc := range []struct {
|
||||
desc string
|
||||
|
@ -24,6 +24,8 @@ import (
|
||||
"go.opentelemetry.io/otel/bridge/opencensus/internal/oc2otel"
|
||||
"go.opentelemetry.io/otel/bridge/opencensus/internal/otel2oc"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"go.opentelemetry.io/otel/trace/embedded"
|
||||
"go.opentelemetry.io/otel/trace/noop"
|
||||
)
|
||||
|
||||
type handler struct{ err error }
|
||||
@ -38,6 +40,8 @@ func withHandler() (*handler, func()) {
|
||||
}
|
||||
|
||||
type tracer struct {
|
||||
embedded.Tracer
|
||||
|
||||
ctx context.Context
|
||||
name string
|
||||
opts []trace.SpanStartOption
|
||||
@ -45,8 +49,8 @@ type tracer struct {
|
||||
|
||||
func (t *tracer) Start(ctx context.Context, name string, opts ...trace.SpanStartOption) (context.Context, trace.Span) {
|
||||
t.ctx, t.name, t.opts = ctx, name, opts
|
||||
noop := trace.NewNoopTracerProvider().Tracer("testing")
|
||||
return noop.Start(ctx, name, opts...)
|
||||
sub := noop.NewTracerProvider().Tracer("testing")
|
||||
return sub.Start(ctx, name, opts...)
|
||||
}
|
||||
|
||||
type ctxKey string
|
||||
@ -110,11 +114,11 @@ func TestTracerFromContext(t *testing.T) {
|
||||
})
|
||||
ctx := trace.ContextWithSpanContext(context.Background(), sc)
|
||||
|
||||
noop := trace.NewNoopTracerProvider().Tracer("TestTracerFromContext")
|
||||
tracer := noop.NewTracerProvider().Tracer("TestTracerFromContext")
|
||||
// Test using the fact that the No-Op span will propagate a span context .
|
||||
ctx, _ = noop.Start(ctx, "test")
|
||||
ctx, _ = tracer.Start(ctx, "test")
|
||||
|
||||
got := internal.NewTracer(noop).FromContext(ctx).SpanContext()
|
||||
got := internal.NewTracer(tracer).FromContext(ctx).SpanContext()
|
||||
// Do not test the convedsion, only that the propagtion.
|
||||
want := otel2oc.SpanContext(sc)
|
||||
if got != want {
|
||||
@ -129,11 +133,11 @@ func TestTracerNewContext(t *testing.T) {
|
||||
})
|
||||
ctx := trace.ContextWithSpanContext(context.Background(), sc)
|
||||
|
||||
noop := trace.NewNoopTracerProvider().Tracer("TestTracerNewContext")
|
||||
tracer := noop.NewTracerProvider().Tracer("TestTracerNewContext")
|
||||
// Test using the fact that the No-Op span will propagate a span context .
|
||||
_, s := noop.Start(ctx, "test")
|
||||
_, s := tracer.Start(ctx, "test")
|
||||
|
||||
ocTracer := internal.NewTracer(noop)
|
||||
ocTracer := internal.NewTracer(tracer)
|
||||
ctx = ocTracer.NewContext(context.Background(), internal.NewSpan(s))
|
||||
got := trace.SpanContextFromContext(ctx)
|
||||
|
||||
|
@ -33,10 +33,11 @@ import (
|
||||
iBaggage "go.opentelemetry.io/otel/internal/baggage"
|
||||
"go.opentelemetry.io/otel/propagation"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"go.opentelemetry.io/otel/trace/noop"
|
||||
)
|
||||
|
||||
var (
|
||||
noopTracer = trace.NewNoopTracerProvider().Tracer("")
|
||||
noopTracer = noop.NewTracerProvider().Tracer("")
|
||||
noopSpan = func() trace.Span {
|
||||
_, s := noopTracer.Start(context.Background(), "")
|
||||
return s
|
||||
|
@ -26,6 +26,8 @@ import (
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.21.0"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"go.opentelemetry.io/otel/trace/embedded"
|
||||
"go.opentelemetry.io/otel/trace/noop"
|
||||
)
|
||||
|
||||
//nolint:revive // ignoring missing comments for unexported global variables in an internal package.
|
||||
@ -44,6 +46,8 @@ type MockContextKeyValue struct {
|
||||
}
|
||||
|
||||
type MockTracer struct {
|
||||
embedded.Tracer
|
||||
|
||||
FinishedSpans []*MockSpan
|
||||
SpareTraceIDs []trace.TraceID
|
||||
SpareSpanIDs []trace.SpanID
|
||||
@ -184,6 +188,8 @@ type MockEvent struct {
|
||||
}
|
||||
|
||||
type MockSpan struct {
|
||||
embedded.Span
|
||||
|
||||
mockTracer *MockTracer
|
||||
officialTracer trace.Tracer
|
||||
spanContext trace.SpanContext
|
||||
@ -295,4 +301,4 @@ func (s *MockSpan) OverrideTracer(tracer trace.Tracer) {
|
||||
s.officialTracer = tracer
|
||||
}
|
||||
|
||||
func (s *MockSpan) TracerProvider() trace.TracerProvider { return trace.NewNoopTracerProvider() }
|
||||
func (s *MockSpan) TracerProvider() trace.TracerProvider { return noop.NewTracerProvider() }
|
||||
|
@ -18,11 +18,14 @@ import (
|
||||
"sync"
|
||||
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"go.opentelemetry.io/otel/trace/embedded"
|
||||
)
|
||||
|
||||
// TracerProvider is an OpenTelemetry TracerProvider that wraps an OpenTracing
|
||||
// Tracer.
|
||||
type TracerProvider struct {
|
||||
embedded.TracerProvider
|
||||
|
||||
bridge *BridgeTracer
|
||||
provider trace.TracerProvider
|
||||
|
||||
|
@ -19,6 +19,7 @@ import (
|
||||
|
||||
"go.opentelemetry.io/otel/bridge/opentracing/internal"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"go.opentelemetry.io/otel/trace/embedded"
|
||||
)
|
||||
|
||||
type namedMockTracer struct {
|
||||
@ -26,7 +27,7 @@ type namedMockTracer struct {
|
||||
*internal.MockTracer
|
||||
}
|
||||
|
||||
type namedMockTracerProvider struct{}
|
||||
type namedMockTracerProvider struct{ embedded.TracerProvider }
|
||||
|
||||
var _ trace.TracerProvider = (*namedMockTracerProvider)(nil)
|
||||
|
||||
|
@ -19,6 +19,7 @@ import (
|
||||
|
||||
"go.opentelemetry.io/otel/bridge/opentracing/migration"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"go.opentelemetry.io/otel/trace/embedded"
|
||||
)
|
||||
|
||||
// WrapperTracerProvider is an OpenTelemetry TracerProvider that wraps an
|
||||
@ -26,6 +27,8 @@ import (
|
||||
//
|
||||
// Deprecated: Use the TracerProvider from NewTracerProvider(...) instead.
|
||||
type WrapperTracerProvider struct {
|
||||
embedded.TracerProvider
|
||||
|
||||
wTracer *WrapperTracer
|
||||
}
|
||||
|
||||
@ -56,6 +59,8 @@ func NewWrappedTracerProvider(bridge *BridgeTracer, tracer trace.Tracer) *Wrappe
|
||||
// aware how to operate in environment where OpenTracing API is also
|
||||
// used.
|
||||
type WrapperTracer struct {
|
||||
embedded.Tracer
|
||||
|
||||
bridge *BridgeTracer
|
||||
tracer trace.Tracer
|
||||
}
|
||||
|
@ -20,9 +20,10 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
"go.opentelemetry.io/otel/metric/noop"
|
||||
metricnoop "go.opentelemetry.io/otel/metric/noop"
|
||||
"go.opentelemetry.io/otel/propagation"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
tracenoop "go.opentelemetry.io/otel/trace/noop"
|
||||
)
|
||||
|
||||
type nonComparableTracerProvider struct {
|
||||
@ -55,7 +56,7 @@ func TestSetTracerProvider(t *testing.T) {
|
||||
t.Run("First Set() should replace the delegate", func(t *testing.T) {
|
||||
ResetForTest(t)
|
||||
|
||||
SetTracerProvider(trace.NewNoopTracerProvider())
|
||||
SetTracerProvider(tracenoop.NewTracerProvider())
|
||||
|
||||
_, ok := TracerProvider().(*tracerProvider)
|
||||
if ok {
|
||||
@ -67,7 +68,7 @@ func TestSetTracerProvider(t *testing.T) {
|
||||
ResetForTest(t)
|
||||
|
||||
tp := TracerProvider()
|
||||
SetTracerProvider(trace.NewNoopTracerProvider())
|
||||
SetTracerProvider(tracenoop.NewTracerProvider())
|
||||
|
||||
ntp := tp.(*tracerProvider)
|
||||
|
||||
@ -153,7 +154,7 @@ func TestSetMeterProvider(t *testing.T) {
|
||||
t.Run("First Set() should replace the delegate", func(t *testing.T) {
|
||||
ResetForTest(t)
|
||||
|
||||
SetMeterProvider(noop.NewMeterProvider())
|
||||
SetMeterProvider(metricnoop.NewMeterProvider())
|
||||
|
||||
_, ok := MeterProvider().(*meterProvider)
|
||||
if ok {
|
||||
@ -166,7 +167,7 @@ func TestSetMeterProvider(t *testing.T) {
|
||||
|
||||
mp := MeterProvider()
|
||||
|
||||
SetMeterProvider(noop.NewMeterProvider())
|
||||
SetMeterProvider(metricnoop.NewMeterProvider())
|
||||
|
||||
dmp := mp.(*meterProvider)
|
||||
|
||||
|
@ -39,6 +39,7 @@ import (
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"go.opentelemetry.io/otel/trace/embedded"
|
||||
)
|
||||
|
||||
// tracerProvider is a placeholder for a configured SDK TracerProvider.
|
||||
@ -46,6 +47,8 @@ import (
|
||||
// All TracerProvider functionality is forwarded to a delegate once
|
||||
// configured.
|
||||
type tracerProvider struct {
|
||||
embedded.TracerProvider
|
||||
|
||||
mtx sync.Mutex
|
||||
tracers map[il]*tracer
|
||||
delegate trace.TracerProvider
|
||||
@ -119,6 +122,8 @@ type il struct {
|
||||
// All Tracer functionality is forwarded to a delegate once configured.
|
||||
// Otherwise, all functionality is forwarded to a NoopTracer.
|
||||
type tracer struct {
|
||||
embedded.Tracer
|
||||
|
||||
name string
|
||||
opts []trace.TracerOption
|
||||
provider *tracerProvider
|
||||
@ -156,6 +161,8 @@ func (t *tracer) Start(ctx context.Context, name string, opts ...trace.SpanStart
|
||||
// SpanContext. It performs no operations other than to return the wrapped
|
||||
// SpanContext.
|
||||
type nonRecordingSpan struct {
|
||||
embedded.Span
|
||||
|
||||
sc trace.SpanContext
|
||||
tracer *tracer
|
||||
}
|
||||
|
@ -23,9 +23,13 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"go.opentelemetry.io/otel/trace/embedded"
|
||||
"go.opentelemetry.io/otel/trace/noop"
|
||||
)
|
||||
|
||||
type fnTracerProvider struct {
|
||||
embedded.TracerProvider
|
||||
|
||||
tracer func(string, ...trace.TracerOption) trace.Tracer
|
||||
}
|
||||
|
||||
@ -34,6 +38,8 @@ func (fn fnTracerProvider) Tracer(instrumentationName string, opts ...trace.Trac
|
||||
}
|
||||
|
||||
type fnTracer struct {
|
||||
embedded.Tracer
|
||||
|
||||
start func(ctx context.Context, spanName string, opts ...trace.SpanStartOption) (context.Context, trace.Span)
|
||||
}
|
||||
|
||||
@ -72,7 +78,7 @@ func TestTraceProviderDelegation(t *testing.T) {
|
||||
assert.Equal(t, want, spanName)
|
||||
}
|
||||
}
|
||||
return trace.NewNoopTracerProvider().Tracer(name).Start(ctx, spanName)
|
||||
return noop.NewTracerProvider().Tracer(name).Start(ctx, spanName)
|
||||
},
|
||||
}
|
||||
},
|
||||
@ -107,7 +113,7 @@ func TestTraceProviderDelegates(t *testing.T) {
|
||||
tracer: func(name string, opts ...trace.TracerOption) trace.Tracer {
|
||||
called = true
|
||||
assert.Equal(t, "abc", name)
|
||||
return trace.NewNoopTracerProvider().Tracer("")
|
||||
return noop.NewTracerProvider().Tracer("")
|
||||
},
|
||||
})
|
||||
|
||||
@ -148,7 +154,7 @@ func TestTraceProviderDelegatesConcurrentSafe(t *testing.T) {
|
||||
// Signal the goroutine to finish.
|
||||
close(quit)
|
||||
}
|
||||
return trace.NewNoopTracerProvider().Tracer("")
|
||||
return noop.NewTracerProvider().Tracer("")
|
||||
},
|
||||
})
|
||||
|
||||
@ -195,7 +201,7 @@ func TestTracerDelegatesConcurrentSafe(t *testing.T) {
|
||||
// Signal the goroutine to finish.
|
||||
close(quit)
|
||||
}
|
||||
return trace.NewNoopTracerProvider().Tracer("").Start(ctx, spanName)
|
||||
return noop.NewTracerProvider().Tracer("").Start(ctx, spanName)
|
||||
},
|
||||
}
|
||||
},
|
||||
@ -218,7 +224,7 @@ func TestTraceProviderDelegatesSameInstance(t *testing.T) {
|
||||
|
||||
SetTracerProvider(fnTracerProvider{
|
||||
tracer: func(name string, opts ...trace.TracerOption) trace.Tracer {
|
||||
return trace.NewNoopTracerProvider().Tracer("")
|
||||
return noop.NewTracerProvider().Tracer("")
|
||||
},
|
||||
})
|
||||
|
||||
|
@ -25,6 +25,8 @@ import (
|
||||
"go.opentelemetry.io/otel/sdk/instrumentation"
|
||||
"go.opentelemetry.io/otel/sdk/resource"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"go.opentelemetry.io/otel/trace/embedded"
|
||||
"go.opentelemetry.io/otel/trace/noop"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -73,6 +75,8 @@ func (cfg tracerProviderConfig) MarshalLog() interface{} {
|
||||
// TracerProvider is an OpenTelemetry TracerProvider. It provides Tracers to
|
||||
// instrumentation so it can trace operational flow through a system.
|
||||
type TracerProvider struct {
|
||||
embedded.TracerProvider
|
||||
|
||||
mu sync.Mutex
|
||||
namedTracer map[instrumentation.Scope]*tracer
|
||||
spanProcessors atomic.Pointer[spanProcessorStates]
|
||||
@ -139,7 +143,7 @@ func NewTracerProvider(opts ...TracerProviderOption) *TracerProvider {
|
||||
func (p *TracerProvider) Tracer(name string, opts ...trace.TracerOption) trace.Tracer {
|
||||
// This check happens before the mutex is acquired to avoid deadlocking if Tracer() is called from within Shutdown().
|
||||
if p.isShutdown.Load() {
|
||||
return trace.NewNoopTracerProvider().Tracer(name, opts...)
|
||||
return noop.NewTracerProvider().Tracer(name, opts...)
|
||||
}
|
||||
c := trace.NewTracerConfig(opts...)
|
||||
if name == "" {
|
||||
@ -157,7 +161,7 @@ func (p *TracerProvider) Tracer(name string, opts ...trace.TracerOption) trace.T
|
||||
// Must check the flag after acquiring the mutex to avoid returning a valid tracer if Shutdown() ran
|
||||
// after the first check above but before we acquired the mutex.
|
||||
if p.isShutdown.Load() {
|
||||
return trace.NewNoopTracerProvider().Tracer(name, opts...), true
|
||||
return noop.NewTracerProvider().Tracer(name, opts...), true
|
||||
}
|
||||
t, ok := p.namedTracer[is]
|
||||
if !ok {
|
||||
|
@ -32,6 +32,7 @@ import (
|
||||
"go.opentelemetry.io/otel/sdk/resource"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.21.0"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"go.opentelemetry.io/otel/trace/embedded"
|
||||
)
|
||||
|
||||
// ReadOnlySpan allows reading information from the data structure underlying a
|
||||
@ -108,6 +109,8 @@ type ReadWriteSpan interface {
|
||||
// recordingSpan is an implementation of the OpenTelemetry Span API
|
||||
// representing the individual component of a trace that is sampled.
|
||||
type recordingSpan struct {
|
||||
embedded.Span
|
||||
|
||||
// mu protects the contents of this span.
|
||||
mu sync.Mutex
|
||||
|
||||
@ -774,6 +777,8 @@ func (s *recordingSpan) runtimeTrace(ctx context.Context) context.Context {
|
||||
// that wraps a SpanContext. It performs no operations other than to return
|
||||
// the wrapped SpanContext or TracerProvider that created it.
|
||||
type nonRecordingSpan struct {
|
||||
embedded.Span
|
||||
|
||||
// tracer is the SDK tracer that created this span.
|
||||
tracer *tracer
|
||||
sc trace.SpanContext
|
||||
|
@ -20,9 +20,12 @@ import (
|
||||
|
||||
"go.opentelemetry.io/otel/sdk/instrumentation"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"go.opentelemetry.io/otel/trace/embedded"
|
||||
)
|
||||
|
||||
type tracer struct {
|
||||
embedded.Tracer
|
||||
|
||||
provider *TracerProvider
|
||||
instrumentationScope instrumentation.Scope
|
||||
}
|
||||
|
64
trace/doc.go
64
trace/doc.go
@ -62,5 +62,69 @@ a default.
|
||||
defer span.End()
|
||||
// ...
|
||||
}
|
||||
|
||||
# API Implementations
|
||||
|
||||
This package does not conform to the standard Go versioning policy; all of its
|
||||
interfaces may have methods added to them without a package major version bump.
|
||||
This non-standard API evolution could surprise an uninformed implementation
|
||||
author. They could unknowingly build their implementation in a way that would
|
||||
result in a runtime panic for their users that update to the new API.
|
||||
|
||||
The API is designed to help inform an instrumentation author about this
|
||||
non-standard API evolution. It requires them to choose a default behavior for
|
||||
unimplemented interface methods. There are three behavior choices they can
|
||||
make:
|
||||
|
||||
- Compilation failure
|
||||
- Panic
|
||||
- Default to another implementation
|
||||
|
||||
All interfaces in this API embed a corresponding interface from
|
||||
[go.opentelemetry.io/otel/trace/embedded]. If an author wants the default
|
||||
behavior of their implementations to be a compilation failure, signaling to
|
||||
their users they need to update to the latest version of that implementation,
|
||||
they need to embed the corresponding interface from
|
||||
[go.opentelemetry.io/otel/trace/embedded] in their implementation. For
|
||||
example,
|
||||
|
||||
import "go.opentelemetry.io/otel/trace/embedded"
|
||||
|
||||
type TracerProvider struct {
|
||||
embedded.TracerProvider
|
||||
// ...
|
||||
}
|
||||
|
||||
If an author wants the default behavior of their implementations to panic, they
|
||||
can embed the API interface directly.
|
||||
|
||||
import "go.opentelemetry.io/otel/trace"
|
||||
|
||||
type TracerProvider struct {
|
||||
trace.TracerProvider
|
||||
// ...
|
||||
}
|
||||
|
||||
This option is not recommended. It will lead to publishing packages that
|
||||
contain runtime panics when users update to newer versions of
|
||||
[go.opentelemetry.io/otel/trace], which may be done with a trasitive
|
||||
dependency.
|
||||
|
||||
Finally, an author can embed another implementation in theirs. The embedded
|
||||
implementation will be used for methods not defined by the author. For example,
|
||||
an author who wants to default to silently dropping the call can use
|
||||
[go.opentelemetry.io/otel/trace/noop]:
|
||||
|
||||
import "go.opentelemetry.io/otel/trace/noop"
|
||||
|
||||
type TracerProvider struct {
|
||||
noop.TracerProvider
|
||||
// ...
|
||||
}
|
||||
|
||||
It is strongly recommended that authors only embed
|
||||
[go.opentelemetry.io/otel/trace/noop] if they choose this default behavior.
|
||||
That implementation is the only one OpenTelemetry authors can guarantee will
|
||||
fully implement all the API interfaces when a user updates their API.
|
||||
*/
|
||||
package trace // import "go.opentelemetry.io/otel/trace"
|
||||
|
56
trace/embedded/embedded.go
Normal file
56
trace/embedded/embedded.go
Normal file
@ -0,0 +1,56 @@
|
||||
// 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 embedded provides interfaces embedded within the [OpenTelemetry
|
||||
// trace API].
|
||||
//
|
||||
// Implementers of the [OpenTelemetry trace API] can embed the relevant type
|
||||
// from this package into their implementation directly. Doing so will result
|
||||
// in a compilation error for users when the [OpenTelemetry trace API] is
|
||||
// extended (which is something that can happen without a major version bump of
|
||||
// the API package).
|
||||
//
|
||||
// [OpenTelemetry trace API]: https://pkg.go.dev/go.opentelemetry.io/otel/trace
|
||||
package embedded // import "go.opentelemetry.io/otel/trace/embedded"
|
||||
|
||||
// TracerProvider is embedded in
|
||||
// [go.opentelemetry.io/otel/trace.TracerProvider].
|
||||
//
|
||||
// Embed this interface in your implementation of the
|
||||
// [go.opentelemetry.io/otel/trace.TracerProvider] if you want users to
|
||||
// experience a compilation error, signaling they need to update to your latest
|
||||
// implementation, when the [go.opentelemetry.io/otel/trace.TracerProvider]
|
||||
// interface is extended (which is something that can happen without a major
|
||||
// version bump of the API package).
|
||||
type TracerProvider interface{ tracerProvider() }
|
||||
|
||||
// Tracer is embedded in [go.opentelemetry.io/otel/trace.Tracer].
|
||||
//
|
||||
// Embed this interface in your implementation of the
|
||||
// [go.opentelemetry.io/otel/trace.Tracer] if you want users to experience a
|
||||
// compilation error, signaling they need to update to your latest
|
||||
// implementation, when the [go.opentelemetry.io/otel/trace.Tracer] interface
|
||||
// is extended (which is something that can happen without a major version bump
|
||||
// of the API package).
|
||||
type Tracer interface{ tracer() }
|
||||
|
||||
// Span is embedded in [go.opentelemetry.io/otel/trace.Span].
|
||||
//
|
||||
// Embed this interface in your implementation of the
|
||||
// [go.opentelemetry.io/otel/trace.Span] if you want users to experience a
|
||||
// compilation error, signaling they need to update to your latest
|
||||
// implementation, when the [go.opentelemetry.io/otel/trace.Span] interface is
|
||||
// extended (which is something that can happen without a major version bump of
|
||||
// the API package).
|
||||
type Span interface{ span() }
|
@ -19,16 +19,20 @@ import (
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/trace/embedded"
|
||||
)
|
||||
|
||||
// NewNoopTracerProvider returns an implementation of TracerProvider that
|
||||
// performs no operations. The Tracer and Spans created from the returned
|
||||
// TracerProvider also perform no operations.
|
||||
//
|
||||
// Deprecated: Use [go.opentelemetry.io/otel/trace/noop.NewTracerProvider]
|
||||
// instead.
|
||||
func NewNoopTracerProvider() TracerProvider {
|
||||
return noopTracerProvider{}
|
||||
}
|
||||
|
||||
type noopTracerProvider struct{}
|
||||
type noopTracerProvider struct{ embedded.TracerProvider }
|
||||
|
||||
var _ TracerProvider = noopTracerProvider{}
|
||||
|
||||
@ -38,7 +42,7 @@ func (p noopTracerProvider) Tracer(string, ...TracerOption) Tracer {
|
||||
}
|
||||
|
||||
// noopTracer is an implementation of Tracer that performs no operations.
|
||||
type noopTracer struct{}
|
||||
type noopTracer struct{ embedded.Tracer }
|
||||
|
||||
var _ Tracer = noopTracer{}
|
||||
|
||||
@ -54,7 +58,7 @@ func (t noopTracer) Start(ctx context.Context, name string, _ ...SpanStartOption
|
||||
}
|
||||
|
||||
// noopSpan is an implementation of Span that performs no operations.
|
||||
type noopSpan struct{}
|
||||
type noopSpan struct{ embedded.Span }
|
||||
|
||||
var _ Span = noopSpan{}
|
||||
|
||||
|
118
trace/noop/noop.go
Normal file
118
trace/noop/noop.go
Normal file
@ -0,0 +1,118 @@
|
||||
// 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 noop provides an implementation of the OpenTelemetry trace API that
|
||||
// produces no telemetry and minimizes used computation resources.
|
||||
//
|
||||
// Using this package to implement the OpenTelemetry trace API will effectively
|
||||
// disable OpenTelemetry.
|
||||
//
|
||||
// This implementation can be embedded in other implementations of the
|
||||
// OpenTelemetry trace API. Doing so will mean the implementation defaults to
|
||||
// no operation for methods it does not implement.
|
||||
package noop // import "go.opentelemetry.io/otel/trace/noop"
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"go.opentelemetry.io/otel/trace/embedded"
|
||||
)
|
||||
|
||||
var (
|
||||
// Compile-time check this implements the OpenTelemetry API.
|
||||
|
||||
_ trace.TracerProvider = TracerProvider{}
|
||||
_ trace.Tracer = Tracer{}
|
||||
_ trace.Span = Span{}
|
||||
)
|
||||
|
||||
// TracerProvider is an OpenTelemetry No-Op TracerProvider.
|
||||
type TracerProvider struct{ embedded.TracerProvider }
|
||||
|
||||
// NewTracerProvider returns a TracerProvider that does not record any telemetry.
|
||||
func NewTracerProvider() TracerProvider {
|
||||
return TracerProvider{}
|
||||
}
|
||||
|
||||
// Tracer returns an OpenTelemetry Tracer that does not record any telemetry.
|
||||
func (TracerProvider) Tracer(string, ...trace.TracerOption) trace.Tracer {
|
||||
return Tracer{}
|
||||
}
|
||||
|
||||
// Tracer is an OpenTelemetry No-Op Tracer.
|
||||
type Tracer struct{ embedded.Tracer }
|
||||
|
||||
// Start creates a span. The created span will be set in a child context of ctx
|
||||
// and returned with the span.
|
||||
//
|
||||
// If ctx contains a span context, the returned span will also contain that
|
||||
// span context. If the span context in ctx is for a non-recording span, that
|
||||
// span instance will be returned directly.
|
||||
func (t Tracer) Start(ctx context.Context, _ string, _ ...trace.SpanStartOption) (context.Context, trace.Span) {
|
||||
span := trace.SpanFromContext(ctx)
|
||||
|
||||
// If the parent context contains a non-zero span context, that span
|
||||
// context needs to be returned as a non-recording span
|
||||
// (https://github.com/open-telemetry/opentelemetry-specification/blob/3a1dde966a4ce87cce5adf464359fe369741bbea/specification/trace/api.md#behavior-of-the-api-in-the-absence-of-an-installed-sdk).
|
||||
var zeroSC trace.SpanContext
|
||||
if sc := span.SpanContext(); !sc.Equal(zeroSC) {
|
||||
if !span.IsRecording() {
|
||||
// If the span is not recording return it directly.
|
||||
return ctx, span
|
||||
}
|
||||
// Otherwise, return the span context needs in a non-recording span.
|
||||
span = Span{sc: sc}
|
||||
} else {
|
||||
// No parent, return a No-Op span with an empty span context.
|
||||
span = Span{}
|
||||
}
|
||||
return trace.ContextWithSpan(ctx, span), span
|
||||
}
|
||||
|
||||
// Span is an OpenTelemetry No-Op Span.
|
||||
type Span struct {
|
||||
embedded.Span
|
||||
|
||||
sc trace.SpanContext
|
||||
}
|
||||
|
||||
// SpanContext returns an empty span context.
|
||||
func (s Span) SpanContext() trace.SpanContext { return s.sc }
|
||||
|
||||
// IsRecording always returns false.
|
||||
func (Span) IsRecording() bool { return false }
|
||||
|
||||
// SetStatus does nothing.
|
||||
func (Span) SetStatus(codes.Code, string) {}
|
||||
|
||||
// SetAttributes does nothing.
|
||||
func (Span) SetAttributes(...attribute.KeyValue) {}
|
||||
|
||||
// End does nothing.
|
||||
func (Span) End(...trace.SpanEndOption) {}
|
||||
|
||||
// RecordError does nothing.
|
||||
func (Span) RecordError(error, ...trace.EventOption) {}
|
||||
|
||||
// AddEvent does nothing.
|
||||
func (Span) AddEvent(string, ...trace.EventOption) {}
|
||||
|
||||
// SetName does nothing.
|
||||
func (Span) SetName(string) {}
|
||||
|
||||
// TracerProvider returns a No-Op TracerProvider.
|
||||
func (Span) TracerProvider() trace.TracerProvider { return TracerProvider{} }
|
117
trace/noop/noop_test.go
Normal file
117
trace/noop/noop_test.go
Normal file
@ -0,0 +1,117 @@
|
||||
// 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 noop // import "go.opentelemetry.io/otel/trace/noop"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
func TestImplementationNoPanics(t *testing.T) {
|
||||
// Check that if type has an embedded interface and that interface has
|
||||
// methods added to it than the No-Op implementation implements them.
|
||||
t.Run("TracerProvider", assertAllExportedMethodNoPanic(
|
||||
reflect.ValueOf(TracerProvider{}),
|
||||
reflect.TypeOf((*trace.TracerProvider)(nil)).Elem(),
|
||||
))
|
||||
t.Run("Meter", assertAllExportedMethodNoPanic(
|
||||
reflect.ValueOf(Tracer{}),
|
||||
reflect.TypeOf((*trace.Tracer)(nil)).Elem(),
|
||||
))
|
||||
t.Run("Span", assertAllExportedMethodNoPanic(
|
||||
reflect.ValueOf(Span{}),
|
||||
reflect.TypeOf((*trace.Span)(nil)).Elem(),
|
||||
))
|
||||
}
|
||||
|
||||
func assertAllExportedMethodNoPanic(rVal reflect.Value, rType reflect.Type) func(*testing.T) {
|
||||
return func(t *testing.T) {
|
||||
for n := 0; n < rType.NumMethod(); n++ {
|
||||
mType := rType.Method(n)
|
||||
if !mType.IsExported() {
|
||||
t.Logf("ignoring unexported %s", mType.Name)
|
||||
continue
|
||||
}
|
||||
m := rVal.MethodByName(mType.Name)
|
||||
if !m.IsValid() {
|
||||
t.Errorf("unknown method for %s: %s", rVal.Type().Name(), mType.Name)
|
||||
}
|
||||
|
||||
numIn := mType.Type.NumIn()
|
||||
if mType.Type.IsVariadic() {
|
||||
numIn--
|
||||
}
|
||||
args := make([]reflect.Value, numIn)
|
||||
ctx := context.Background()
|
||||
for i := range args {
|
||||
aType := mType.Type.In(i)
|
||||
if aType.Name() == "Context" {
|
||||
// Do not panic on a nil context.
|
||||
args[i] = reflect.ValueOf(ctx)
|
||||
} else {
|
||||
args[i] = reflect.New(aType).Elem()
|
||||
}
|
||||
}
|
||||
|
||||
assert.NotPanicsf(t, func() {
|
||||
_ = m.Call(args)
|
||||
}, "%s.%s", rVal.Type().Name(), mType.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewTracerProvider(t *testing.T) {
|
||||
tp := NewTracerProvider()
|
||||
assert.Equal(t, tp, TracerProvider{})
|
||||
tracer := tp.Tracer("")
|
||||
assert.Equal(t, tracer, Tracer{})
|
||||
}
|
||||
|
||||
func TestTracerStartPropagatesSpanContext(t *testing.T) {
|
||||
tracer := NewTracerProvider().Tracer("")
|
||||
spanCtx := trace.SpanContext{}
|
||||
|
||||
ctx := trace.ContextWithSpanContext(context.Background(), spanCtx)
|
||||
ctx, span := tracer.Start(ctx, "test_span")
|
||||
assert.Equal(t, spanCtx, trace.SpanContextFromContext(ctx), "empty span context not set in context")
|
||||
assert.IsType(t, Span{}, span, "non-noop span returned")
|
||||
assert.Equal(t, spanCtx, span.SpanContext(), "empty span context not returned from span")
|
||||
assert.False(t, span.IsRecording(), "empty span context returned recording span")
|
||||
|
||||
spanCtx = spanCtx.WithTraceID(trace.TraceID([16]byte{1}))
|
||||
spanCtx = spanCtx.WithSpanID(trace.SpanID([8]byte{1}))
|
||||
ctx = trace.ContextWithSpanContext(context.Background(), spanCtx)
|
||||
ctx, span = tracer.Start(ctx, "test_span")
|
||||
assert.Equal(t, spanCtx, trace.SpanContextFromContext(ctx), "non-empty span context not set in context")
|
||||
assert.Equal(t, spanCtx, span.SpanContext(), "non-empty span context not returned from span")
|
||||
assert.False(t, span.IsRecording(), "non-empty span context returned recording span")
|
||||
|
||||
rSpan := recordingSpan{Span: Span{sc: spanCtx}}
|
||||
ctx = trace.ContextWithSpan(context.Background(), rSpan)
|
||||
ctx, span = tracer.Start(ctx, "test_span")
|
||||
assert.Equal(t, spanCtx, trace.SpanContextFromContext(ctx), "recording span's span context not set in context")
|
||||
assert.IsType(t, Span{}, span, "non-noop span returned")
|
||||
assert.Equal(t, spanCtx, span.SpanContext(), "recording span's span context not returned from span")
|
||||
assert.False(t, span.IsRecording(), "recording span returned")
|
||||
}
|
||||
|
||||
type recordingSpan struct{ Span }
|
||||
|
||||
func (recordingSpan) IsRecording() bool { return true }
|
@ -22,6 +22,7 @@ import (
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/trace/embedded"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -342,8 +343,15 @@ func (sc SpanContext) MarshalJSON() ([]byte, error) {
|
||||
// create a Span and it is then up to the operation the Span represents to
|
||||
// properly end the Span when the operation itself ends.
|
||||
//
|
||||
// Warning: methods may be added to this interface in minor releases.
|
||||
// Warning: Methods may be added to this interface in minor releases. See
|
||||
// package documentation on API implementation for information on how to set
|
||||
// default behavior for unimplemented methods.
|
||||
type Span interface {
|
||||
// Users of the interface can ignore this. This embedded type is only used
|
||||
// by implementations of this interface. See the "API Implementations"
|
||||
// section of the package documentation for more information.
|
||||
embedded.Span
|
||||
|
||||
// End completes the Span. The Span is considered complete and ready to be
|
||||
// delivered through the rest of the telemetry pipeline after this method
|
||||
// is called. Therefore, updates to the Span are not allowed after this
|
||||
@ -490,8 +498,15 @@ func (sk SpanKind) String() string {
|
||||
|
||||
// Tracer is the creator of Spans.
|
||||
//
|
||||
// Warning: methods may be added to this interface in minor releases.
|
||||
// Warning: Methods may be added to this interface in minor releases. See
|
||||
// package documentation on API implementation for information on how to set
|
||||
// default behavior for unimplemented methods.
|
||||
type Tracer interface {
|
||||
// Users of the interface can ignore this. This embedded type is only used
|
||||
// by implementations of this interface. See the "API Implementations"
|
||||
// section of the package documentation for more information.
|
||||
embedded.Tracer
|
||||
|
||||
// Start creates a span and a context.Context containing the newly-created span.
|
||||
//
|
||||
// If the context.Context provided in `ctx` contains a Span then the newly-created
|
||||
@ -522,8 +537,15 @@ type Tracer interface {
|
||||
// at runtime from its users or it can simply use the globally registered one
|
||||
// (see https://pkg.go.dev/go.opentelemetry.io/otel#GetTracerProvider).
|
||||
//
|
||||
// Warning: methods may be added to this interface in minor releases.
|
||||
// Warning: Methods may be added to this interface in minor releases. See
|
||||
// package documentation on API implementation for information on how to set
|
||||
// default behavior for unimplemented methods.
|
||||
type TracerProvider interface {
|
||||
// Users of the interface can ignore this. This embedded type is only used
|
||||
// by implementations of this interface. See the "API Implementations"
|
||||
// section of the package documentation for more information.
|
||||
embedded.TracerProvider
|
||||
|
||||
// Tracer returns a unique Tracer scoped to be used by instrumentation code
|
||||
// to trace computational workflows. The scope and identity of that
|
||||
// instrumentation code is uniquely defined by the name and options passed.
|
||||
|
@ -20,19 +20,21 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"go.opentelemetry.io/otel/trace/embedded"
|
||||
"go.opentelemetry.io/otel/trace/noop"
|
||||
)
|
||||
|
||||
type testTracerProvider struct{}
|
||||
type testTracerProvider struct{ embedded.TracerProvider }
|
||||
|
||||
var _ trace.TracerProvider = &testTracerProvider{}
|
||||
|
||||
func (*testTracerProvider) Tracer(_ string, _ ...trace.TracerOption) trace.Tracer {
|
||||
return trace.NewNoopTracerProvider().Tracer("")
|
||||
return noop.NewTracerProvider().Tracer("")
|
||||
}
|
||||
|
||||
func TestMultipleGlobalTracerProvider(t *testing.T) {
|
||||
p1 := testTracerProvider{}
|
||||
p2 := trace.NewNoopTracerProvider()
|
||||
p2 := noop.NewTracerProvider()
|
||||
SetTracerProvider(&p1)
|
||||
SetTracerProvider(p2)
|
||||
|
||||
|
Reference in New Issue
Block a user