1
0
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:
Tyler Yahn
2025-08-18 01:37:38 -07:00
committed by GitHub
parent 9488493043
commit 150f6b4dbc
3 changed files with 103 additions and 28 deletions
+35 -3
View File
@@ -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.
+68
View File
@@ -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
}
-25
View File
@@ -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