You've already forked opentelemetry-go
mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2026-06-03 18:35:08 +02:00
Flatten tracer.initSelfObservability into TracerProvider.Tracer (#7205)
- Do not use [side-effect programming](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) to setup a `tracer`. Make the setup explicit, unique, and local. - Add the `newInst` function to ensure DRY and scope instrument creation
This commit is contained in:
+35
-3
@@ -5,14 +5,20 @@ package trace // import "go.opentelemetry.io/otel/sdk/trace"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/internal/global"
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
"go.opentelemetry.io/otel/sdk"
|
||||
"go.opentelemetry.io/otel/sdk/instrumentation"
|
||||
"go.opentelemetry.io/otel/sdk/resource"
|
||||
"go.opentelemetry.io/otel/sdk/trace/internal/x"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.36.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.36.0/otelconv"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"go.opentelemetry.io/otel/trace/embedded"
|
||||
"go.opentelemetry.io/otel/trace/noop"
|
||||
@@ -157,10 +163,19 @@ func (p *TracerProvider) Tracer(name string, opts ...trace.TracerOption) trace.T
|
||||
t, ok := p.namedTracer[is]
|
||||
if !ok {
|
||||
t = &tracer{
|
||||
provider: p,
|
||||
instrumentationScope: is,
|
||||
provider: p,
|
||||
instrumentationScope: is,
|
||||
selfObservabilityEnabled: x.SelfObservability.Enabled(),
|
||||
}
|
||||
if t.selfObservabilityEnabled {
|
||||
var err error
|
||||
t.spanLiveMetric, t.spanStartedMetric, err = newInst()
|
||||
if err != nil {
|
||||
msg := "failed to create self-observability metrics for tracer: %w"
|
||||
err := fmt.Errorf(msg, err)
|
||||
otel.Handle(err)
|
||||
}
|
||||
}
|
||||
t.initSelfObservability()
|
||||
p.namedTracer[is] = t
|
||||
}
|
||||
return t, ok
|
||||
@@ -186,6 +201,23 @@ func (p *TracerProvider) Tracer(name string, opts ...trace.TracerOption) trace.T
|
||||
return t
|
||||
}
|
||||
|
||||
func newInst() (otelconv.SDKSpanLive, otelconv.SDKSpanStarted, error) {
|
||||
m := otel.GetMeterProvider().Meter(
|
||||
selfObsScopeName,
|
||||
metric.WithInstrumentationVersion(sdk.Version()),
|
||||
metric.WithSchemaURL(semconv.SchemaURL),
|
||||
)
|
||||
|
||||
var err error
|
||||
spanLiveMetric, e := otelconv.NewSDKSpanLive(m)
|
||||
err = errors.Join(err, e)
|
||||
|
||||
spanStartedMetric, e := otelconv.NewSDKSpanStarted(m)
|
||||
err = errors.Join(err, e)
|
||||
|
||||
return spanLiveMetric, spanStartedMetric, err
|
||||
}
|
||||
|
||||
// RegisterSpanProcessor adds the given SpanProcessor to the list of SpanProcessors.
|
||||
func (p *TracerProvider) RegisterSpanProcessor(sp SpanProcessor) {
|
||||
// This check prevents calls during a shutdown.
|
||||
|
||||
@@ -13,7 +13,9 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
@@ -398,3 +400,69 @@ func TestTracerProviderReturnsSameTracer(t *testing.T) {
|
||||
assert.Same(t, t1, t4)
|
||||
assert.Same(t, t2, t5)
|
||||
}
|
||||
|
||||
func TestTracerProviderSelfObservability(t *testing.T) {
|
||||
handler.Reset()
|
||||
p := NewTracerProvider()
|
||||
|
||||
// Enable self-observability
|
||||
t.Setenv("OTEL_GO_X_SELF_OBSERVABILITY", "true")
|
||||
|
||||
tr := p.Tracer("test-tracer")
|
||||
require.IsType(t, &tracer{}, tr)
|
||||
|
||||
tStruct := tr.(*tracer)
|
||||
assert.True(t, tStruct.selfObservabilityEnabled, "Self-observability should be enabled")
|
||||
|
||||
// Verify instruments are created
|
||||
assert.NotNil(t, tStruct.spanLiveMetric, "spanLiveMetric should be created")
|
||||
assert.NotNil(t, tStruct.spanStartedMetric, "spanStartedMetric should be created")
|
||||
|
||||
// Verify errors are passed to the otel handler
|
||||
handlerErrs := handler.errs
|
||||
assert.Empty(t, handlerErrs, "No errors should occur during instrument creation")
|
||||
}
|
||||
|
||||
func TestTracerProviderSelfObservabilityErrorsHandled(t *testing.T) {
|
||||
handler.Reset()
|
||||
|
||||
orig := otel.GetMeterProvider()
|
||||
t.Cleanup(func() { otel.SetMeterProvider(orig) })
|
||||
otel.SetMeterProvider(&errMeterProvider{err: assert.AnError})
|
||||
|
||||
p := NewTracerProvider()
|
||||
|
||||
// Enable self-observability
|
||||
t.Setenv("OTEL_GO_X_SELF_OBSERVABILITY", "true")
|
||||
|
||||
// Create a tracer to trigger instrument creation.
|
||||
tr := p.Tracer("test-tracer")
|
||||
_ = tr
|
||||
|
||||
require.Len(t, handler.errs, 1)
|
||||
assert.ErrorIs(t, handler.errs[0], assert.AnError)
|
||||
}
|
||||
|
||||
type errMeterProvider struct {
|
||||
metric.MeterProvider
|
||||
|
||||
err error
|
||||
}
|
||||
|
||||
func (mp *errMeterProvider) Meter(string, ...metric.MeterOption) metric.Meter {
|
||||
return &errMeter{err: mp.err}
|
||||
}
|
||||
|
||||
type errMeter struct {
|
||||
metric.Meter
|
||||
|
||||
err error
|
||||
}
|
||||
|
||||
func (m *errMeter) Int64Counter(string, ...metric.Int64CounterOption) (metric.Int64Counter, error) {
|
||||
return nil, m.err
|
||||
}
|
||||
|
||||
func (m *errMeter) Int64UpDownCounter(string, ...metric.Int64UpDownCounterOption) (metric.Int64UpDownCounter, error) {
|
||||
return nil, m.err
|
||||
}
|
||||
|
||||
@@ -7,13 +7,8 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
"go.opentelemetry.io/otel/sdk"
|
||||
"go.opentelemetry.io/otel/sdk/instrumentation"
|
||||
"go.opentelemetry.io/otel/sdk/trace/internal/x"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.36.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.36.0/otelconv"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"go.opentelemetry.io/otel/trace/embedded"
|
||||
@@ -32,26 +27,6 @@ type tracer struct {
|
||||
|
||||
var _ trace.Tracer = &tracer{}
|
||||
|
||||
func (tr *tracer) initSelfObservability() {
|
||||
if !x.SelfObservability.Enabled() {
|
||||
return
|
||||
}
|
||||
|
||||
tr.selfObservabilityEnabled = true
|
||||
mp := otel.GetMeterProvider()
|
||||
m := mp.Meter(selfObsScopeName,
|
||||
metric.WithInstrumentationVersion(sdk.Version()),
|
||||
metric.WithSchemaURL(semconv.SchemaURL))
|
||||
|
||||
var err error
|
||||
if tr.spanLiveMetric, err = otelconv.NewSDKSpanLive(m); err != nil {
|
||||
otel.Handle(err)
|
||||
}
|
||||
if tr.spanStartedMetric, err = otelconv.NewSDKSpanStarted(m); err != nil {
|
||||
otel.Handle(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Start starts a Span and returns it along with a context containing it.
|
||||
//
|
||||
// The Span is created with the provided name and as a child of any existing
|
||||
|
||||
Reference in New Issue
Block a user