Skip to content

Commit 5fb833a

Browse files
committed
add OTEL_GO_X_SELF_OBSERVABILITY feature gate, and otel.sdk.batch_span_processor.queue_size metric
1 parent 14b874e commit 5fb833a

File tree

5 files changed

+65
-3
lines changed

5 files changed

+65
-3
lines changed

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
88

99
## [Unreleased]
1010

11+
### Added
12+
13+
- Add OTEL_GO_X_SELF_OBSERVABILITY envirionment variable to control whether self-observability metrics and traces are produced by SDKs.
14+
- Add experimental otel.sdk.batch_span_processor.queue_size metric to the trace batch span processor.
15+
1116
### Fixed
1217

1318
- Relax minimum Go version to 1.22.0 in various modules. (#6073)

sdk/internal/x/README.md

+7
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,13 @@ All other values are ignored.
2222
[OpenTelemetry resource semantic conventions]: https://opentelemetry.io/docs/specs/semconv/resource/
2323
[resource detectors]: https://pkg.go.dev/go.opentelemetry.io/otel/sdk/resource#Detector
2424

25+
### SDK Self-Observability
26+
27+
To enable experimental metric and trace instrumentation in SDKs, set the `OTEL_GO_X_SELF_OBSERVABILITY` environment variable.
28+
If enabled, this instrumentation uses the global `TracerProvider` and `MeterProvider`.
29+
The value set must be the case-insensitive string of `"true"` to enable the feature.
30+
All other values are ignored.
31+
2532
#### Examples
2633

2734
Enable experimental resource semantic conventions.

sdk/internal/x/x.go

+13
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,19 @@ var Resource = newFeature("RESOURCE", func(v string) (string, bool) {
2525
return "", false
2626
})
2727

28+
// SelfObservability is an experimental feature flag that determines if SDK
29+
// self-observability metrics are enabled.
30+
//
31+
// To enable this feature set the OTEL_GO_X_SELF_OBSERVABILITY environment variable
32+
// to the case-insensitive string value of "true" (i.e. "True" and "TRUE"
33+
// will also enable this).
34+
var SelfObservability = newFeature("SELF_OBSERVABILITY", func(v string) (string, bool) {
35+
if strings.ToLower(v) == "true" {
36+
return v, true
37+
}
38+
return "", false
39+
})
40+
2841
// Feature is an experimental feature control flag. It provides a uniform way
2942
// to interact with these feature flags and parse their values.
3043
type Feature[T any] struct {

sdk/trace/batch_span_processor.go

+39-3
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,17 @@ package trace // import "go.opentelemetry.io/otel/sdk/trace"
55

66
import (
77
"context"
8+
"errors"
89
"sync"
910
"sync/atomic"
1011
"time"
1112

1213
"go.opentelemetry.io/otel"
1314
"go.opentelemetry.io/otel/internal/global"
15+
"go.opentelemetry.io/otel/metric"
16+
"go.opentelemetry.io/otel/metric/noop"
1417
"go.opentelemetry.io/otel/sdk/internal/env"
18+
"go.opentelemetry.io/otel/sdk/internal/x"
1519
"go.opentelemetry.io/otel/trace"
1620
)
1721

@@ -63,8 +67,10 @@ type batchSpanProcessor struct {
6367
e SpanExporter
6468
o BatchSpanProcessorOptions
6569

66-
queue chan ReadOnlySpan
67-
dropped uint32
70+
queue chan ReadOnlySpan
71+
dropped uint32
72+
processedCounter metric.Int64Counter
73+
callbackRegistration metric.Registration
6874

6975
batch []ReadOnlySpan
7076
batchMutex sync.Mutex
@@ -111,6 +117,8 @@ func NewBatchSpanProcessor(exporter SpanExporter, options ...BatchSpanProcessorO
111117
stopCh: make(chan struct{}),
112118
}
113119

120+
bsp.configureSelfObservability()
121+
114122
bsp.stopWait.Add(1)
115123
go func() {
116124
defer bsp.stopWait.Done()
@@ -121,6 +129,34 @@ func NewBatchSpanProcessor(exporter SpanExporter, options ...BatchSpanProcessorO
121129
return bsp
122130
}
123131

132+
func (bsp *batchSpanProcessor) configureSelfObservability() {
133+
mp := otel.GetMeterProvider()
134+
if !x.SelfObservability.Enabled() {
135+
mp = metric.MeterProvider(noop.NewMeterProvider())
136+
}
137+
meter := mp.Meter(
138+
selfObsScopeName,
139+
metric.WithInstrumentationVersion(version()),
140+
)
141+
142+
queueSizeCounter, err := meter.Int64ObservableUpDownCounter("otel.sdk.batch_span_processor.queue_size",
143+
metric.WithUnit("{span}"),
144+
metric.WithDescription("The number of ended spans currently enqueued by the processor."),
145+
)
146+
if err != nil {
147+
otel.Handle(err)
148+
}
149+
bsp.callbackRegistration, err = meter.RegisterCallback(
150+
func(ctx context.Context, o metric.Observer) error {
151+
o.ObserveInt64(queueSizeCounter, int64(len(bsp.queue)))
152+
return nil
153+
},
154+
queueSizeCounter)
155+
if err != nil {
156+
otel.Handle(err)
157+
}
158+
}
159+
124160
// OnStart method does nothing.
125161
func (bsp *batchSpanProcessor) OnStart(parent context.Context, s ReadWriteSpan) {}
126162

@@ -162,7 +198,7 @@ func (bsp *batchSpanProcessor) Shutdown(ctx context.Context) error {
162198
err = ctx.Err()
163199
}
164200
})
165-
return err
201+
return errors.Join(err, bsp.callbackRegistration.Unregister())
166202
}
167203

168204
type forceFlushSpan struct {

sdk/trace/provider.go

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020

2121
const (
2222
defaultTracerName = "go.opentelemetry.io/otel/sdk/tracer"
23+
selfObsScopeName = "go.opentelemetry.io/otel/sdk/trace"
2324
)
2425

2526
// tracerProviderConfig.

0 commit comments

Comments
 (0)