From f7c180ec8f7a330d3b5384cde7b96a051a920d88 Mon Sep 17 00:00:00 2001 From: Mahendra Bishnoi Date: Sat, 9 Aug 2025 19:15:48 +0530 Subject: [PATCH 01/30] implementation of `otel.sdk.processor.span.processed` metric for simple_span_processor.go --- CHANGELOG.md | 4 +-- sdk/trace/internal/x/README.md | 1 + sdk/trace/provider.go | 1 + sdk/trace/simple_span_processor.go | 54 +++++++++++++++++++++++++++++- 4 files changed, 57 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f8d1a4f91ef..4a59e7595c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,8 +46,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - The `go.opentelemetry.io/otel/semconv/v1.36.0` package. The package contains semantic conventions from the `v1.36.0` version of the OpenTelemetry Semantic Conventions. See the [migration documentation](./semconv/v1.36.0/MIGRATION.md) for information on how to upgrade from `go.opentelemetry.io/otel/semconv/v1.34.0.`(#7032) -- Add experimental self-observability span metrics in `go.opentelemetry.io/otel/sdk/trace`. - Check the `go.opentelemetry.io/otel/sdk/trace/internal/x` package documentation for more information. (#7027) +- Add experimental self-observability span and simple span processor metrics in `go.opentelemetry.io/otel/sdk/trace`. + Check the `go.opentelemetry.io/otel/sdk/trace/internal/x` package documentation for more information. (#7027, #7158) - Add native histogram exemplar support in `go.opentelemetry.io/otel/exporters/prometheus`. (#6772) - Add experimental self-observability log metrics in `go.opentelemetry.io/otel/sdk/log`. Check the `go.opentelemetry.io/otel/sdk/log/internal/x` package documentation for more information. (#7121) diff --git a/sdk/trace/internal/x/README.md b/sdk/trace/internal/x/README.md index 4c79e80a7ce..2a4e100c937 100644 --- a/sdk/trace/internal/x/README.md +++ b/sdk/trace/internal/x/README.md @@ -20,6 +20,7 @@ When enabled, the SDK will create the following metrics using the global `MeterP - `otel.sdk.span.live` - `otel.sdk.span.started` +- `otel.sdk.processor.span.processed` (only for simple span processor) Please see the [Semantic conventions for OpenTelemetry SDK metrics] documentation for more details on these metrics. diff --git a/sdk/trace/provider.go b/sdk/trace/provider.go index fd942d23e80..62e87fd631e 100644 --- a/sdk/trace/provider.go +++ b/sdk/trace/provider.go @@ -20,6 +20,7 @@ import ( const ( defaultTracerName = "go.opentelemetry.io/otel/sdk/tracer" + selfObsScopeName = "go.opentelemetry.io/otel/sdk/trace" ) // tracerProviderConfig. diff --git a/sdk/trace/simple_span_processor.go b/sdk/trace/simple_span_processor.go index 411d9ccdd78..5288854b7ab 100644 --- a/sdk/trace/simple_span_processor.go +++ b/sdk/trace/simple_span_processor.go @@ -5,10 +5,18 @@ package trace // import "go.opentelemetry.io/otel/sdk/trace" import ( "context" + "fmt" "sync" + "sync/atomic" "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/internal/global" + "go.opentelemetry.io/otel/metric" + "go.opentelemetry.io/otel/sdk" + "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" ) // simpleSpanProcessor is a SpanProcessor that synchronously sends all @@ -17,6 +25,10 @@ type simpleSpanProcessor struct { exporterMu sync.Mutex exporter SpanExporter stopOnce sync.Once + + selfObservabilityEnabled bool + componentNameAttr attribute.KeyValue + spansProcessedCounter otelconv.SDKProcessorSpanProcessed } var _ SpanProcessor = (*simpleSpanProcessor)(nil) @@ -33,11 +45,42 @@ func NewSimpleSpanProcessor(exporter SpanExporter) SpanProcessor { ssp := &simpleSpanProcessor{ exporter: exporter, } + ssp.configureSelfObservability() + global.Warn("SimpleSpanProcessor is not recommended for production use, consider using BatchSpanProcessor instead.") return ssp } +var processorIDCounter atomic.Int64 + +// nextProcessorID returns an identifier for this simple span processor, +// starting with 0 and incrementing by 1 each time it is called. +func nextProcessorID() int64 { + return processorIDCounter.Add(1) - 1 +} + +// configureSelfObservability configures metrics for the simple span processor. +func (ssp *simpleSpanProcessor) configureSelfObservability() { + if !x.SelfObservability.Enabled() { + return + } + ssp.selfObservabilityEnabled = true + ssp.componentNameAttr = semconv.OTelComponentName( + fmt.Sprintf("%s/%d", otelconv.ComponentTypeBatchingSpanProcessor, nextProcessorID())) + meter := otel.GetMeterProvider().Meter( + selfObsScopeName, + metric.WithInstrumentationVersion(sdk.Version()), + metric.WithSchemaURL(semconv.SchemaURL), + ) + + var err error + ssp.spansProcessedCounter, err = otelconv.NewSDKProcessorSpanProcessed(meter) + if err != nil { + otel.Handle(err) + } +} + // OnStart does nothing. func (*simpleSpanProcessor) OnStart(context.Context, ReadWriteSpan) {} @@ -47,8 +90,17 @@ func (ssp *simpleSpanProcessor) OnEnd(s ReadOnlySpan) { defer ssp.exporterMu.Unlock() if ssp.exporter != nil && s.SpanContext().TraceFlags().IsSampled() { - if err := ssp.exporter.ExportSpans(context.Background(), []ReadOnlySpan{s}); err != nil { + attrs := []attribute.KeyValue{ + ssp.componentNameAttr, + ssp.spansProcessedCounter.AttrComponentType(otelconv.ComponentTypeSimpleSpanProcessor), + } + err := ssp.exporter.ExportSpans(context.Background(), []ReadOnlySpan{s}) + if err != nil { otel.Handle(err) + attrs = append(attrs, semconv.ErrorType(err)) + } + if ssp.selfObservabilityEnabled { + ssp.spansProcessedCounter.Add(context.Background(), 1, attrs...) } } } From f15ae4e233b06cfd953f726fe6f90355f06dc6b5 Mon Sep 17 00:00:00 2001 From: Mahendra Bishnoi Date: Sat, 9 Aug 2025 19:26:11 +0530 Subject: [PATCH 02/30] add correct PR number --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a59e7595c5..6fe9cf287a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,7 +47,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm The package contains semantic conventions from the `v1.36.0` version of the OpenTelemetry Semantic Conventions. See the [migration documentation](./semconv/v1.36.0/MIGRATION.md) for information on how to upgrade from `go.opentelemetry.io/otel/semconv/v1.34.0.`(#7032) - Add experimental self-observability span and simple span processor metrics in `go.opentelemetry.io/otel/sdk/trace`. - Check the `go.opentelemetry.io/otel/sdk/trace/internal/x` package documentation for more information. (#7027, #7158) + Check the `go.opentelemetry.io/otel/sdk/trace/internal/x` package documentation for more information. (#7027, #7162) - Add native histogram exemplar support in `go.opentelemetry.io/otel/exporters/prometheus`. (#6772) - Add experimental self-observability log metrics in `go.opentelemetry.io/otel/sdk/log`. Check the `go.opentelemetry.io/otel/sdk/log/internal/x` package documentation for more information. (#7121) From d743f6a8ee6eb53d0b94838d6107253eca14e889 Mon Sep 17 00:00:00 2001 From: Mahendra Bishnoi Date: Sat, 9 Aug 2025 19:46:40 +0530 Subject: [PATCH 03/30] fix component type --- sdk/trace/simple_span_processor.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/trace/simple_span_processor.go b/sdk/trace/simple_span_processor.go index 5288854b7ab..0b53812e587 100644 --- a/sdk/trace/simple_span_processor.go +++ b/sdk/trace/simple_span_processor.go @@ -67,7 +67,7 @@ func (ssp *simpleSpanProcessor) configureSelfObservability() { } ssp.selfObservabilityEnabled = true ssp.componentNameAttr = semconv.OTelComponentName( - fmt.Sprintf("%s/%d", otelconv.ComponentTypeBatchingSpanProcessor, nextProcessorID())) + fmt.Sprintf("%s/%d", otelconv.ComponentTypeSimpleSpanProcessor, nextProcessorID())) meter := otel.GetMeterProvider().Meter( selfObsScopeName, metric.WithInstrumentationVersion(sdk.Version()), From 17eb6c716a0e5403aa348b9bc7e46cbe1b308cde Mon Sep 17 00:00:00 2001 From: Mahendra Bishnoi Date: Sat, 16 Aug 2025 17:56:02 +0530 Subject: [PATCH 04/30] processorIDCounter -> simpleProcessorIDCounter as processorIDCounter is already being used in batch span processor --- sdk/trace/simple_span_processor.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sdk/trace/simple_span_processor.go b/sdk/trace/simple_span_processor.go index 0b53812e587..24600f711e3 100644 --- a/sdk/trace/simple_span_processor.go +++ b/sdk/trace/simple_span_processor.go @@ -52,12 +52,12 @@ func NewSimpleSpanProcessor(exporter SpanExporter) SpanProcessor { return ssp } -var processorIDCounter atomic.Int64 +var simpleProcessorIDCounter atomic.Int64 -// nextProcessorID returns an identifier for this simple span processor, +// nextSimpleProcessorID returns an identifier for this simple span processor, // starting with 0 and incrementing by 1 each time it is called. -func nextProcessorID() int64 { - return processorIDCounter.Add(1) - 1 +func nextSimpleProcessorID() int64 { + return simpleProcessorIDCounter.Add(1) - 1 } // configureSelfObservability configures metrics for the simple span processor. @@ -67,7 +67,7 @@ func (ssp *simpleSpanProcessor) configureSelfObservability() { } ssp.selfObservabilityEnabled = true ssp.componentNameAttr = semconv.OTelComponentName( - fmt.Sprintf("%s/%d", otelconv.ComponentTypeSimpleSpanProcessor, nextProcessorID())) + fmt.Sprintf("%s/%d", otelconv.ComponentTypeSimpleSpanProcessor, nextSimpleProcessorID())) meter := otel.GetMeterProvider().Meter( selfObsScopeName, metric.WithInstrumentationVersion(sdk.Version()), From f8aa01e98fe502e7d70121c75452020563086b32 Mon Sep 17 00:00:00 2001 From: Mahendra Bishnoi Date: Sat, 16 Aug 2025 19:48:01 +0530 Subject: [PATCH 05/30] test cases for simple span processor --- sdk/trace/simple_span_processor_test.go | 148 ++++++++++++++++++++++++ 1 file changed, 148 insertions(+) diff --git a/sdk/trace/simple_span_processor_test.go b/sdk/trace/simple_span_processor_test.go index 4aa4ea39c88..a401284ad31 100644 --- a/sdk/trace/simple_span_processor_test.go +++ b/sdk/trace/simple_span_processor_test.go @@ -6,11 +6,23 @@ package trace import ( "context" "errors" + "strconv" "sync" "testing" "time" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/sdk" + "go.opentelemetry.io/otel/sdk/instrumentation" + "go.opentelemetry.io/otel/sdk/metric" + "go.opentelemetry.io/otel/sdk/metric/metricdata" + "go.opentelemetry.io/otel/sdk/metric/metricdata/metricdatatest" + semconv "go.opentelemetry.io/otel/semconv/v1.36.0" + "go.opentelemetry.io/otel/semconv/v1.36.0/otelconv" ) type simpleTestExporter struct { @@ -34,6 +46,17 @@ func (t *simpleTestExporter) Shutdown(ctx context.Context) error { } } +var _ SpanExporter = (*failingTestExporter)(nil) + +type failingTestExporter struct { + simpleTestExporter +} + +func (f *failingTestExporter) ExportSpans(ctx context.Context, spans []ReadOnlySpan) error { + _ = f.simpleTestExporter.ExportSpans(ctx, spans) + return errors.New("failed to export spans") +} + var _ SpanExporter = (*simpleTestExporter)(nil) func TestNewSimpleSpanProcessor(t *testing.T) { @@ -168,3 +191,128 @@ func TestSimpleSpanProcessorShutdownHonorsContextCancel(t *testing.T) { t.Errorf("SimpleSpanProcessor.Shutdown did not return %v, got %v", want, got) } } + +func TestSimpleSpanProcessorSelfObservability(t *testing.T) { + tests := []struct { + name string + enabled bool + exporter SpanExporter + assertMetrics func(t *testing.T, rm metricdata.ResourceMetrics) + }{ + { + name: "Disabled", + enabled: false, + exporter: &simpleTestExporter{}, + assertMetrics: func(t *testing.T, rm metricdata.ResourceMetrics) { + assert.Empty(t, rm.ScopeMetrics) + }, + }, + { + name: "Enabled", + enabled: true, + exporter: &simpleTestExporter{}, + assertMetrics: func(t *testing.T, rm metricdata.ResourceMetrics) { + assert.Len(t, rm.ScopeMetrics, 1) + sm := rm.ScopeMetrics[0] + + want := metricdata.ScopeMetrics{ + Scope: instrumentation.Scope{ + Name: "go.opentelemetry.io/otel/sdk/trace", + Version: sdk.Version(), + SchemaURL: semconv.SchemaURL, + }, + Metrics: []metricdata.Metrics{ + { + Name: otelconv.SDKProcessorSpanProcessed{}.Name(), + Description: otelconv.SDKProcessorSpanProcessed{}.Description(), + Unit: otelconv.SDKProcessorSpanProcessed{}.Unit(), + Data: metricdata.Sum[int64]{ + DataPoints: []metricdata.DataPoint[int64]{ + { + Value: 1, + Attributes: attribute.NewSet( + semconv.OTelComponentName("simple_span_processor/0"), + semconv.OTelComponentTypeKey.String("simple_span_processor"), + ), + }, + }, + Temporality: metricdata.CumulativeTemporality, + IsMonotonic: true, + }, + }, + }, + } + + metricdatatest.AssertEqual(t, want, sm, metricdatatest.IgnoreTimestamp()) + }, + }, + { + name: "Enabled, Exporter error", + enabled: true, + exporter: &failingTestExporter{ + simpleTestExporter: simpleTestExporter{}, + }, + assertMetrics: func(t *testing.T, rm metricdata.ResourceMetrics) { + assert.Len(t, rm.ScopeMetrics, 1) + sm := rm.ScopeMetrics[0] + + want := metricdata.ScopeMetrics{ + Scope: instrumentation.Scope{ + Name: "go.opentelemetry.io/otel/sdk/trace", + Version: sdk.Version(), + SchemaURL: semconv.SchemaURL, + }, + Metrics: []metricdata.Metrics{ + { + Name: otelconv.SDKProcessorSpanProcessed{}.Name(), + Description: otelconv.SDKProcessorSpanProcessed{}.Description(), + Unit: otelconv.SDKProcessorSpanProcessed{}.Unit(), + Data: metricdata.Sum[int64]{ + DataPoints: []metricdata.DataPoint[int64]{ + { + Value: 1, + Attributes: attribute.NewSet( + semconv.OTelComponentName("simple_span_processor/1"), + semconv.OTelComponentTypeKey.String("simple_span_processor"), + semconv.ErrorTypeKey.String("*errors.errorString"), + ), + }, + }, + Temporality: metricdata.CumulativeTemporality, + IsMonotonic: true, + }, + }, + }, + } + + metricdatatest.AssertEqual(t, want, sm, metricdatatest.IgnoreTimestamp()) + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + t.Setenv("OTEL_GO_X_SELF_OBSERVABILITY", strconv.FormatBool(test.enabled)) + + original := otel.GetMeterProvider() + t.Cleanup(func() { otel.SetMeterProvider(original) }) + + r := metric.NewManualReader() + mp := metric.NewMeterProvider( + metric.WithReader(r), + metric.WithView(dropSpanMetricsView), + ) + otel.SetMeterProvider(mp) + + ssp := NewSimpleSpanProcessor(test.exporter) + tp := basicTracerProvider(t) + tp.RegisterSpanProcessor(ssp) + startSpan(tp, test.name).End() + + var rm metricdata.ResourceMetrics + require.NoError(t, r.Collect(context.Background(), &rm)) + test.assertMetrics(t, rm) + }) + + } +} From 5c0fafb0442604bf44cdf1836f0a3344dadeab94 Mon Sep 17 00:00:00 2001 From: Mahendra Bishnoi Date: Sat, 16 Aug 2025 19:54:35 +0530 Subject: [PATCH 06/30] fix lint --- sdk/trace/simple_span_processor_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/sdk/trace/simple_span_processor_test.go b/sdk/trace/simple_span_processor_test.go index a401284ad31..d415859cf08 100644 --- a/sdk/trace/simple_span_processor_test.go +++ b/sdk/trace/simple_span_processor_test.go @@ -313,6 +313,5 @@ func TestSimpleSpanProcessorSelfObservability(t *testing.T) { require.NoError(t, r.Collect(context.Background(), &rm)) test.assertMetrics(t, rm) }) - } } From 6f72bbb327bf1d07c8bb1e1fee3c9c44e4a57c2a Mon Sep 17 00:00:00 2001 From: Mahendra Bishnoi Date: Sat, 16 Aug 2025 22:14:54 +0530 Subject: [PATCH 07/30] review comments - separate changelog entry - flatten self observability initialization - reset simpleProcessorIDCounter so tests can be run in parallel and order doesn't matter --- CHANGELOG.md | 6 +++-- sdk/trace/simple_span_processor.go | 34 +++++++++++++------------ sdk/trace/simple_span_processor_test.go | 3 ++- 3 files changed, 24 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c3c8dea46c8..c486fd0e554 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,14 +49,16 @@ The next release will require at least [Go 1.24]. - The `go.opentelemetry.io/otel/semconv/v1.36.0` package. The package contains semantic conventions from the `v1.36.0` version of the OpenTelemetry Semantic Conventions. See the [migration documentation](./semconv/v1.36.0/MIGRATION.md) for information on how to upgrade from `go.opentelemetry.io/otel/semconv/v1.34.0.`(#7032) -- Add experimental self-observability span, simple span processor, and batch span processor metrics in `go.opentelemetry.io/otel/sdk/trace`. - Check the `go.opentelemetry.io/otel/sdk/trace/internal/x` package documentation for more information. (#7027, #6393, #7162) +- Add experimental self-observability span and batch span processor metrics in `go.opentelemetry.io/otel/sdk/trace`. + Check the `go.opentelemetry.io/otel/sdk/trace/internal/x` package documentation for more information. (#7027, #6393) - Add native histogram exemplar support in `go.opentelemetry.io/otel/exporters/prometheus`. (#6772) - Add experimental self-observability log metrics in `go.opentelemetry.io/otel/sdk/log`. Check the `go.opentelemetry.io/otel/sdk/log/internal/x` package documentation for more information. (#7121) - Add experimental self-observability trace exporter metrics in `go.opentelemetry.io/otel/exporters/stdout/stdouttrace`. Check the `go.opentelemetry.io/otel/exporters/stdout/stdouttrace/internal/x` package documentation for more information. (#7133) - Support testing of [Go 1.25]. (#7187) +- Add experimental self-observability simple span processor metrics in `go.opentelemetry.io/otel/sdk/trace`. + Check the `go.opentelemetry.io/otel/sdk/trace/internal/x` package documentation for more information. (#7162) ### Changed diff --git a/sdk/trace/simple_span_processor.go b/sdk/trace/simple_span_processor.go index 24600f711e3..8d6f35a2797 100644 --- a/sdk/trace/simple_span_processor.go +++ b/sdk/trace/simple_span_processor.go @@ -43,9 +43,22 @@ var _ SpanProcessor = (*simpleSpanProcessor)(nil) // use instead. func NewSimpleSpanProcessor(exporter SpanExporter) SpanProcessor { ssp := &simpleSpanProcessor{ - exporter: exporter, + exporter: exporter, + selfObservabilityEnabled: x.SelfObservability.Enabled(), + } + + if ssp.selfObservabilityEnabled { + ssp.componentNameAttr = semconv.OTelComponentName( + fmt.Sprintf("%s/%d", otelconv.ComponentTypeSimpleSpanProcessor, nextSimpleProcessorID())) + + var err error + ssp.spansProcessedCounter, err = newInst() + if err != nil { + msg := "failed to create self-observability metrics for simple span processor: %w" + err := fmt.Errorf(msg, err) + otel.Handle(err) + } } - ssp.configureSelfObservability() global.Warn("SimpleSpanProcessor is not recommended for production use, consider using BatchSpanProcessor instead.") @@ -60,25 +73,14 @@ func nextSimpleProcessorID() int64 { return simpleProcessorIDCounter.Add(1) - 1 } -// configureSelfObservability configures metrics for the simple span processor. -func (ssp *simpleSpanProcessor) configureSelfObservability() { - if !x.SelfObservability.Enabled() { - return - } - ssp.selfObservabilityEnabled = true - ssp.componentNameAttr = semconv.OTelComponentName( - fmt.Sprintf("%s/%d", otelconv.ComponentTypeSimpleSpanProcessor, nextSimpleProcessorID())) +func newInst() (otelconv.SDKProcessorSpanProcessed, error) { meter := otel.GetMeterProvider().Meter( selfObsScopeName, metric.WithInstrumentationVersion(sdk.Version()), metric.WithSchemaURL(semconv.SchemaURL), ) - - var err error - ssp.spansProcessedCounter, err = otelconv.NewSDKProcessorSpanProcessed(meter) - if err != nil { - otel.Handle(err) - } + spansProcessedCounter, err := otelconv.NewSDKProcessorSpanProcessed(meter) + return spansProcessedCounter, err } // OnStart does nothing. diff --git a/sdk/trace/simple_span_processor_test.go b/sdk/trace/simple_span_processor_test.go index d415859cf08..99796043372 100644 --- a/sdk/trace/simple_span_processor_test.go +++ b/sdk/trace/simple_span_processor_test.go @@ -272,7 +272,7 @@ func TestSimpleSpanProcessorSelfObservability(t *testing.T) { { Value: 1, Attributes: attribute.NewSet( - semconv.OTelComponentName("simple_span_processor/1"), + semconv.OTelComponentName("simple_span_processor/0"), semconv.OTelComponentTypeKey.String("simple_span_processor"), semconv.ErrorTypeKey.String("*errors.errorString"), ), @@ -312,6 +312,7 @@ func TestSimpleSpanProcessorSelfObservability(t *testing.T) { var rm metricdata.ResourceMetrics require.NoError(t, r.Collect(context.Background(), &rm)) test.assertMetrics(t, rm) + simpleProcessorIDCounter.Store(0) // reset simpleProcessorIDCounter }) } } From ec0b88a759440715b28b5288157a56a7b8fef831 Mon Sep 17 00:00:00 2001 From: Mahendra Bishnoi Date: Sat, 16 Aug 2025 22:53:09 +0530 Subject: [PATCH 08/30] review comments - use sync.Pool to amortize allocation for metric attrs --- sdk/trace/simple_span_processor.go | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/sdk/trace/simple_span_processor.go b/sdk/trace/simple_span_processor.go index 8d6f35a2797..9c91e7fb252 100644 --- a/sdk/trace/simple_span_processor.go +++ b/sdk/trace/simple_span_processor.go @@ -19,6 +19,17 @@ import ( "go.opentelemetry.io/otel/semconv/v1.36.0/otelconv" ) +var measureAttrsPool = sync.Pool{ + New: func() any { + // "component.name" + "component.type" + "error.type" + const n = 1 + 1 + 1 + s := make([]attribute.KeyValue, 0, n) + // Return a pointer to a slice instead of a slice itself + // to avoid allocations on every call. + return &s + }, +} + // simpleSpanProcessor is a SpanProcessor that synchronously sends all // completed Spans to a trace.Exporter immediately. type simpleSpanProcessor struct { @@ -92,17 +103,22 @@ func (ssp *simpleSpanProcessor) OnEnd(s ReadOnlySpan) { defer ssp.exporterMu.Unlock() if ssp.exporter != nil && s.SpanContext().TraceFlags().IsSampled() { - attrs := []attribute.KeyValue{ + attrs := measureAttrsPool.Get().(*[]attribute.KeyValue) + defer func() { + *attrs = (*attrs)[:0] // reset the slice for reuse + measureAttrsPool.Put(attrs) + }() + *attrs = append(*attrs, ssp.componentNameAttr, - ssp.spansProcessedCounter.AttrComponentType(otelconv.ComponentTypeSimpleSpanProcessor), - } + ssp.spansProcessedCounter.AttrComponentType(otelconv.ComponentTypeSimpleSpanProcessor)) + err := ssp.exporter.ExportSpans(context.Background(), []ReadOnlySpan{s}) if err != nil { otel.Handle(err) - attrs = append(attrs, semconv.ErrorType(err)) + *attrs = append(*attrs, semconv.ErrorType(err)) } if ssp.selfObservabilityEnabled { - ssp.spansProcessedCounter.Add(context.Background(), 1, attrs...) + ssp.spansProcessedCounter.Add(context.Background(), 1, *attrs...) } } } From 966d7d1bfa62eab73bc53ea4db5c087a998b8882 Mon Sep 17 00:00:00 2001 From: Mahendra Bishnoi Date: Sat, 16 Aug 2025 23:07:19 +0530 Subject: [PATCH 09/30] review comments - use sync.Pool to amortize allocation for metric attrs - pass context with span to ensure metric is recorded with correct span context --- sdk/trace/simple_span_processor.go | 6 +++++- sdk/trace/simple_span_processor_test.go | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/sdk/trace/simple_span_processor.go b/sdk/trace/simple_span_processor.go index 9c91e7fb252..962b126faa3 100644 --- a/sdk/trace/simple_span_processor.go +++ b/sdk/trace/simple_span_processor.go @@ -17,6 +17,7 @@ import ( "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" ) var measureAttrsPool = sync.Pool{ @@ -118,7 +119,10 @@ func (ssp *simpleSpanProcessor) OnEnd(s ReadOnlySpan) { *attrs = append(*attrs, semconv.ErrorType(err)) } if ssp.selfObservabilityEnabled { - ssp.spansProcessedCounter.Add(context.Background(), 1, *attrs...) + // Add the span to the context to ensure the metric is recorded + // with the correct span context. + ctx := trace.ContextWithSpanContext(context.Background(), s.SpanContext()) + ssp.spansProcessedCounter.Add(ctx, 1, *attrs...) } } } diff --git a/sdk/trace/simple_span_processor_test.go b/sdk/trace/simple_span_processor_test.go index 99796043372..e0245919e0c 100644 --- a/sdk/trace/simple_span_processor_test.go +++ b/sdk/trace/simple_span_processor_test.go @@ -243,7 +243,7 @@ func TestSimpleSpanProcessorSelfObservability(t *testing.T) { }, } - metricdatatest.AssertEqual(t, want, sm, metricdatatest.IgnoreTimestamp()) + metricdatatest.AssertEqual(t, want, sm, metricdatatest.IgnoreTimestamp(), metricdatatest.IgnoreExemplars()) }, }, { @@ -285,7 +285,7 @@ func TestSimpleSpanProcessorSelfObservability(t *testing.T) { }, } - metricdatatest.AssertEqual(t, want, sm, metricdatatest.IgnoreTimestamp()) + metricdatatest.AssertEqual(t, want, sm, metricdatatest.IgnoreTimestamp(), metricdatatest.IgnoreExemplars()) }, }, } From df565dfe7ff17313fcce33bd68b61638db25795c Mon Sep 17 00:00:00 2001 From: Mahendra Bishnoi Date: Sat, 16 Aug 2025 23:37:17 +0530 Subject: [PATCH 10/30] run `make precommit` --- sdk/trace/simple_span_processor_test.go | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/sdk/trace/simple_span_processor_test.go b/sdk/trace/simple_span_processor_test.go index e0245919e0c..bc5f8926dca 100644 --- a/sdk/trace/simple_span_processor_test.go +++ b/sdk/trace/simple_span_processor_test.go @@ -243,7 +243,13 @@ func TestSimpleSpanProcessorSelfObservability(t *testing.T) { }, } - metricdatatest.AssertEqual(t, want, sm, metricdatatest.IgnoreTimestamp(), metricdatatest.IgnoreExemplars()) + metricdatatest.AssertEqual( + t, + want, + sm, + metricdatatest.IgnoreTimestamp(), + metricdatatest.IgnoreExemplars(), + ) }, }, { @@ -285,7 +291,13 @@ func TestSimpleSpanProcessorSelfObservability(t *testing.T) { }, } - metricdatatest.AssertEqual(t, want, sm, metricdatatest.IgnoreTimestamp(), metricdatatest.IgnoreExemplars()) + metricdatatest.AssertEqual( + t, + want, + sm, + metricdatatest.IgnoreTimestamp(), + metricdatatest.IgnoreExemplars(), + ) }, }, } From 97c11ff10cb3037f445c3f5633848e3b3226184c Mon Sep 17 00:00:00 2001 From: Mahendra Bishnoi Date: Thu, 28 Aug 2025 23:37:37 +0530 Subject: [PATCH 11/30] fix issue caused by merge with main due to function name collision --- sdk/trace/simple_span_processor.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/trace/simple_span_processor.go b/sdk/trace/simple_span_processor.go index 962b126faa3..7465a5d54f9 100644 --- a/sdk/trace/simple_span_processor.go +++ b/sdk/trace/simple_span_processor.go @@ -64,7 +64,7 @@ func NewSimpleSpanProcessor(exporter SpanExporter) SpanProcessor { fmt.Sprintf("%s/%d", otelconv.ComponentTypeSimpleSpanProcessor, nextSimpleProcessorID())) var err error - ssp.spansProcessedCounter, err = newInst() + ssp.spansProcessedCounter, err = newSpanProcessedInst() if err != nil { msg := "failed to create self-observability metrics for simple span processor: %w" err := fmt.Errorf(msg, err) @@ -85,7 +85,7 @@ func nextSimpleProcessorID() int64 { return simpleProcessorIDCounter.Add(1) - 1 } -func newInst() (otelconv.SDKProcessorSpanProcessed, error) { +func newSpanProcessedInst() (otelconv.SDKProcessorSpanProcessed, error) { meter := otel.GetMeterProvider().Meter( selfObsScopeName, metric.WithInstrumentationVersion(sdk.Version()), From 9753d0f5578d002511c7992a3e05f238a3964bb9 Mon Sep 17 00:00:00 2001 From: Mahendra Bishnoi Date: Wed, 17 Sep 2025 16:37:17 +0530 Subject: [PATCH 12/30] fix changelog -> move to unreleased section --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 95c614e4ece..7f5c495533b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Add `WithInstrumentationAttributeSet` option to `go.opentelemetry.io/otel/log`, `go.opentelemetry.io/otel/metric`, and `go.opentelemetry.io/otel/trace` packages. This provides a concurrent-safe and performant alternative to `WithInstrumentationAttributes` by accepting a pre-constructed `attribute.Set`. (#7287) - Greatly reduce the cost of recording metrics in `go.opentelemetry.io/otel/sdk/metric` using hashing for map keys. (#7175) +- Add experimental self-observability simple span processor metrics in `go.opentelemetry.io/otel/sdk/trace`. + Check the `go.opentelemetry.io/otel/sdk/trace/internal/x` package documentation for more information. (#7162) ### Fixed @@ -90,8 +92,6 @@ The next release will require at least [Go 1.24]. - The `go.opentelemetry.io/otel/semconv/v1.37.0` package. The package contains semantic conventions from the `v1.37.0` version of the OpenTelemetry Semantic Conventions. See the [migration documentation](./semconv/v1.37.0/MIGRATION.md) for information on how to upgrade from `go.opentelemetry.io/otel/semconv/v1.36.0.`(#7254) -- Add experimental self-observability simple span processor metrics in `go.opentelemetry.io/otel/sdk/trace`. - Check the `go.opentelemetry.io/otel/sdk/trace/internal/x` package documentation for more information. (#7162) ### Changed From 754b9bbb881b0dccb995205505391d21408ec30e Mon Sep 17 00:00:00 2001 From: Mahendra Bishnoi Date: Wed, 17 Sep 2025 18:18:42 +0530 Subject: [PATCH 13/30] move sdk observability out of simpleSpanProcessor struct --- .../internal/observ/simple_span_processor.go | 75 +++++++++++++++++++ sdk/trace/simple_span_processor.go | 65 +++------------- sdk/trace/simple_span_processor_test.go | 8 +- 3 files changed, 88 insertions(+), 60 deletions(-) create mode 100644 sdk/trace/internal/observ/simple_span_processor.go diff --git a/sdk/trace/internal/observ/simple_span_processor.go b/sdk/trace/internal/observ/simple_span_processor.go new file mode 100644 index 00000000000..5d82691896c --- /dev/null +++ b/sdk/trace/internal/observ/simple_span_processor.go @@ -0,0 +1,75 @@ +package observ + +import ( + "context" + "fmt" + "sync" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric" + "go.opentelemetry.io/otel/sdk" + "go.opentelemetry.io/otel/sdk/trace/internal/x" + semconv "go.opentelemetry.io/otel/semconv/v1.37.0" + "go.opentelemetry.io/otel/semconv/v1.37.0/otelconv" +) + +var measureAttrsPool = sync.Pool{ + New: func() any { + // "component.name" + "component.type" + "error.type" + const n = 1 + 1 + 1 + s := make([]attribute.KeyValue, 0, n) + // Return a pointer to a slice instead of a slice itself + // to avoid allocations on every call. + return &s + }, +} + +type SSP struct { + componentNameAttr attribute.KeyValue + spansProcessedCounter otelconv.SDKProcessorSpanProcessed +} + +// SSPComponentName returns the component name attribute for a +// SimpleSpanProcessor with the given ID. +func SSPComponentName(id int64) attribute.KeyValue { + t := otelconv.ComponentTypeSimpleSpanProcessor + name := fmt.Sprintf("%s/%d", t, id) + return semconv.OTelComponentName(name) +} + +func NewSSP(id int64) (*SSP, error) { + if !x.Observability.Enabled() { + return nil, nil + } + + meter := otel.GetMeterProvider().Meter( + ScopeName, + metric.WithInstrumentationVersion(sdk.Version()), + metric.WithSchemaURL(SchemaURL), + ) + componentName := SSPComponentName(id) + spansProcessedCounter, err := otelconv.NewSDKProcessorSpanProcessed(meter) + if err != nil { + err = fmt.Errorf("failed to create SSP processed spans metric: %w", err) + } + return &SSP{ + componentNameAttr: componentName, + spansProcessedCounter: spansProcessedCounter, + }, err +} + +func (ssp *SSP) Record(ctx context.Context, count int64, err error) { + attrs := measureAttrsPool.Get().(*[]attribute.KeyValue) + defer func() { + *attrs = (*attrs)[:0] // reset the slice for reuse + measureAttrsPool.Put(attrs) + }() + if err != nil { + *attrs = append(*attrs, semconv.ErrorType(err)) + } + *attrs = append(*attrs, + ssp.componentNameAttr, + ssp.spansProcessedCounter.AttrComponentType(otelconv.ComponentTypeSimpleSpanProcessor)) + ssp.spansProcessedCounter.Add(ctx, count, *attrs...) +} diff --git a/sdk/trace/simple_span_processor.go b/sdk/trace/simple_span_processor.go index 7465a5d54f9..162084389e3 100644 --- a/sdk/trace/simple_span_processor.go +++ b/sdk/trace/simple_span_processor.go @@ -5,32 +5,15 @@ package trace // import "go.opentelemetry.io/otel/sdk/trace" import ( "context" - "fmt" "sync" "sync/atomic" "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/internal/global" - "go.opentelemetry.io/otel/metric" - "go.opentelemetry.io/otel/sdk" - "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/sdk/trace/internal/observ" "go.opentelemetry.io/otel/trace" ) -var measureAttrsPool = sync.Pool{ - New: func() any { - // "component.name" + "component.type" + "error.type" - const n = 1 + 1 + 1 - s := make([]attribute.KeyValue, 0, n) - // Return a pointer to a slice instead of a slice itself - // to avoid allocations on every call. - return &s - }, -} - // simpleSpanProcessor is a SpanProcessor that synchronously sends all // completed Spans to a trace.Exporter immediately. type simpleSpanProcessor struct { @@ -38,9 +21,7 @@ type simpleSpanProcessor struct { exporter SpanExporter stopOnce sync.Once - selfObservabilityEnabled bool - componentNameAttr attribute.KeyValue - spansProcessedCounter otelconv.SDKProcessorSpanProcessed + inst *observ.SSP } var _ SpanProcessor = (*simpleSpanProcessor)(nil) @@ -55,21 +36,13 @@ var _ SpanProcessor = (*simpleSpanProcessor)(nil) // use instead. func NewSimpleSpanProcessor(exporter SpanExporter) SpanProcessor { ssp := &simpleSpanProcessor{ - exporter: exporter, - selfObservabilityEnabled: x.SelfObservability.Enabled(), + exporter: exporter, } - if ssp.selfObservabilityEnabled { - ssp.componentNameAttr = semconv.OTelComponentName( - fmt.Sprintf("%s/%d", otelconv.ComponentTypeSimpleSpanProcessor, nextSimpleProcessorID())) - - var err error - ssp.spansProcessedCounter, err = newSpanProcessedInst() - if err != nil { - msg := "failed to create self-observability metrics for simple span processor: %w" - err := fmt.Errorf(msg, err) - otel.Handle(err) - } + var err error + ssp.inst, err = observ.NewSSP(nextSimpleProcessorID()) + if err != nil { + otel.Handle(err) } global.Warn("SimpleSpanProcessor is not recommended for production use, consider using BatchSpanProcessor instead.") @@ -85,16 +58,6 @@ func nextSimpleProcessorID() int64 { return simpleProcessorIDCounter.Add(1) - 1 } -func newSpanProcessedInst() (otelconv.SDKProcessorSpanProcessed, error) { - meter := otel.GetMeterProvider().Meter( - selfObsScopeName, - metric.WithInstrumentationVersion(sdk.Version()), - metric.WithSchemaURL(semconv.SchemaURL), - ) - spansProcessedCounter, err := otelconv.NewSDKProcessorSpanProcessed(meter) - return spansProcessedCounter, err -} - // OnStart does nothing. func (*simpleSpanProcessor) OnStart(context.Context, ReadWriteSpan) {} @@ -104,25 +67,15 @@ func (ssp *simpleSpanProcessor) OnEnd(s ReadOnlySpan) { defer ssp.exporterMu.Unlock() if ssp.exporter != nil && s.SpanContext().TraceFlags().IsSampled() { - attrs := measureAttrsPool.Get().(*[]attribute.KeyValue) - defer func() { - *attrs = (*attrs)[:0] // reset the slice for reuse - measureAttrsPool.Put(attrs) - }() - *attrs = append(*attrs, - ssp.componentNameAttr, - ssp.spansProcessedCounter.AttrComponentType(otelconv.ComponentTypeSimpleSpanProcessor)) - err := ssp.exporter.ExportSpans(context.Background(), []ReadOnlySpan{s}) if err != nil { otel.Handle(err) - *attrs = append(*attrs, semconv.ErrorType(err)) } - if ssp.selfObservabilityEnabled { + if ssp.inst != nil { // Add the span to the context to ensure the metric is recorded // with the correct span context. ctx := trace.ContextWithSpanContext(context.Background(), s.SpanContext()) - ssp.spansProcessedCounter.Add(ctx, 1, *attrs...) + ssp.inst.Record(ctx, 1, err) } } } diff --git a/sdk/trace/simple_span_processor_test.go b/sdk/trace/simple_span_processor_test.go index bc5f8926dca..828b469fa13 100644 --- a/sdk/trace/simple_span_processor_test.go +++ b/sdk/trace/simple_span_processor_test.go @@ -21,8 +21,8 @@ import ( "go.opentelemetry.io/otel/sdk/metric" "go.opentelemetry.io/otel/sdk/metric/metricdata" "go.opentelemetry.io/otel/sdk/metric/metricdata/metricdatatest" - semconv "go.opentelemetry.io/otel/semconv/v1.36.0" - "go.opentelemetry.io/otel/semconv/v1.36.0/otelconv" + semconv "go.opentelemetry.io/otel/semconv/v1.37.0" + "go.opentelemetry.io/otel/semconv/v1.37.0/otelconv" ) type simpleTestExporter struct { @@ -217,7 +217,7 @@ func TestSimpleSpanProcessorSelfObservability(t *testing.T) { want := metricdata.ScopeMetrics{ Scope: instrumentation.Scope{ - Name: "go.opentelemetry.io/otel/sdk/trace", + Name: "go.opentelemetry.io/otel/sdk/trace/internal/observ", Version: sdk.Version(), SchemaURL: semconv.SchemaURL, }, @@ -264,7 +264,7 @@ func TestSimpleSpanProcessorSelfObservability(t *testing.T) { want := metricdata.ScopeMetrics{ Scope: instrumentation.Scope{ - Name: "go.opentelemetry.io/otel/sdk/trace", + Name: "go.opentelemetry.io/otel/sdk/trace/internal/observ", Version: sdk.Version(), SchemaURL: semconv.SchemaURL, }, From 5545ac679aa0f8bf1cf8b7bc14eef674d92b9188 Mon Sep 17 00:00:00 2001 From: Mahendra Bishnoi Date: Wed, 17 Sep 2025 18:22:38 +0530 Subject: [PATCH 14/30] run make precommit --- CHANGELOG.md | 2 +- sdk/trace/internal/observ/simple_span_processor.go | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f5c495533b..f070d2ab074 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm This provides a concurrent-safe and performant alternative to `WithInstrumentationAttributes` by accepting a pre-constructed `attribute.Set`. (#7287) - Greatly reduce the cost of recording metrics in `go.opentelemetry.io/otel/sdk/metric` using hashing for map keys. (#7175) - Add experimental self-observability simple span processor metrics in `go.opentelemetry.io/otel/sdk/trace`. - Check the `go.opentelemetry.io/otel/sdk/trace/internal/x` package documentation for more information. (#7162) + Check the `go.opentelemetry.io/otel/sdk/trace/internal/x` package documentation for more information. (#7374) ### Fixed diff --git a/sdk/trace/internal/observ/simple_span_processor.go b/sdk/trace/internal/observ/simple_span_processor.go index 5d82691896c..bd2b61c3205 100644 --- a/sdk/trace/internal/observ/simple_span_processor.go +++ b/sdk/trace/internal/observ/simple_span_processor.go @@ -1,4 +1,7 @@ -package observ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package observ // import "go.opentelemetry.io/otel/sdk/trace/internal/observ" import ( "context" From e855db1f025359e01071c3f54ad1a20ac3f21829 Mon Sep 17 00:00:00 2001 From: Mahendra Bishnoi Date: Sat, 27 Sep 2025 11:35:38 +0530 Subject: [PATCH 15/30] Record -> SpanProcessed --- sdk/trace/internal/observ/simple_span_processor.go | 2 +- sdk/trace/simple_span_processor.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/trace/internal/observ/simple_span_processor.go b/sdk/trace/internal/observ/simple_span_processor.go index bd2b61c3205..81d2ab6f0a9 100644 --- a/sdk/trace/internal/observ/simple_span_processor.go +++ b/sdk/trace/internal/observ/simple_span_processor.go @@ -62,7 +62,7 @@ func NewSSP(id int64) (*SSP, error) { }, err } -func (ssp *SSP) Record(ctx context.Context, count int64, err error) { +func (ssp *SSP) SpanProcessed(ctx context.Context, count int64, err error) { attrs := measureAttrsPool.Get().(*[]attribute.KeyValue) defer func() { *attrs = (*attrs)[:0] // reset the slice for reuse diff --git a/sdk/trace/simple_span_processor.go b/sdk/trace/simple_span_processor.go index 162084389e3..88ff6261017 100644 --- a/sdk/trace/simple_span_processor.go +++ b/sdk/trace/simple_span_processor.go @@ -75,7 +75,7 @@ func (ssp *simpleSpanProcessor) OnEnd(s ReadOnlySpan) { // Add the span to the context to ensure the metric is recorded // with the correct span context. ctx := trace.ContextWithSpanContext(context.Background(), s.SpanContext()) - ssp.inst.Record(ctx, 1, err) + ssp.inst.SpanProcessed(ctx, 1, err) } } } From 558eb43bac2cf3727f21d93e53ecfa4ec4e71099 Mon Sep 17 00:00:00 2001 From: Mahendra Bishnoi Date: Sat, 27 Sep 2025 12:34:25 +0530 Subject: [PATCH 16/30] add tests for simple_span_processor in observ package --- .../observ/simple_span_processor_test.go | 72 +++++++++++++++++++ sdk/trace/simple_span_processor_test.go | 2 +- 2 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 sdk/trace/internal/observ/simple_span_processor_test.go diff --git a/sdk/trace/internal/observ/simple_span_processor_test.go b/sdk/trace/internal/observ/simple_span_processor_test.go new file mode 100644 index 00000000000..336522b8eec --- /dev/null +++ b/sdk/trace/internal/observ/simple_span_processor_test.go @@ -0,0 +1,72 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 +package observ_test + +import ( + "errors" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/sdk/trace/internal/observ" + semconv "go.opentelemetry.io/otel/semconv/v1.37.0" + "testing" +) + +const sspComponentId = 0 + +func TestSSPComponentName(t *testing.T) { + got := observ.SSPComponentName(10) + want := semconv.OTelComponentName("simple_span_processor/10") + assert.Equal(t, want, got) +} + +func TestNewSSPError(t *testing.T) { + t.Setenv("OTEL_GO_X_OBSERVABILITY", "true") + + orig := otel.GetMeterProvider() + t.Cleanup(func() { otel.SetMeterProvider(orig) }) + + mp := &errMeterProvider{err: assert.AnError} + otel.SetMeterProvider(mp) + + _, err := observ.NewSSP(sspComponentId) + require.ErrorIs(t, err, assert.AnError, "new instrument errors") + assert.ErrorContains(t, err, "create SSP processed spans metric") +} + +func TestNewSSPDisabled(t *testing.T) { + ssp, err := observ.NewSSP(sspComponentId) + assert.NoError(t, err) + assert.Nil(t, ssp) +} + +func TestSSPSpanProcessed(t *testing.T) { + ctx := t.Context() + collect := setup(t) + ssp, err := observ.NewSSP(sspComponentId) + assert.NoError(t, err) + + ssp.SpanProcessed(ctx, 1, nil) + check(t, collect(), processed(dPt(sspSet(), 1))) + ssp.SpanProcessed(ctx, 2, nil) + check(t, collect(), processed(dPt(sspSet(), 3))) + + processErr := errors.New("error processing span") + ssp.SpanProcessed(ctx, 1, processErr) + check(t, collect(), processed( + dPt(sspSet(), 3), + dPt(sspSet(semconv.ErrorType(processErr)), 1), + )) +} + +func BenchmarkSSP(b *testing.B) { + b.Setenv("OTEL_GO_X_OBSERVABILITY", "true") +} + +func sspSet(attrs ...attribute.KeyValue) attribute.Set { + return attribute.NewSet(append([]attribute.KeyValue{ + semconv.OTelComponentTypeSimpleSpanProcessor, + observ.SSPComponentName(sspComponentId), + }, attrs...)...) +} diff --git a/sdk/trace/simple_span_processor_test.go b/sdk/trace/simple_span_processor_test.go index 07e4ce10a4b..098ea5cca28 100644 --- a/sdk/trace/simple_span_processor_test.go +++ b/sdk/trace/simple_span_processor_test.go @@ -322,7 +322,7 @@ func TestSimpleSpanProcessorSelfObservability(t *testing.T) { startSpan(tp, test.name).End() var rm metricdata.ResourceMetrics - require.NoError(t, r.Collect(context.Background(), &rm)) + require.NoError(t, r.Collect(t.Context(), &rm)) test.assertMetrics(t, rm) simpleProcessorIDCounter.Store(0) // reset simpleProcessorIDCounter }) From 323bc44c65afc7bff431ee92a29e3a3c94291ec0 Mon Sep 17 00:00:00 2001 From: Mahendra Bishnoi Date: Sat, 27 Sep 2025 13:12:53 +0530 Subject: [PATCH 17/30] fix formatting --- sdk/trace/internal/observ/simple_span_processor_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sdk/trace/internal/observ/simple_span_processor_test.go b/sdk/trace/internal/observ/simple_span_processor_test.go index 336522b8eec..e56590fca37 100644 --- a/sdk/trace/internal/observ/simple_span_processor_test.go +++ b/sdk/trace/internal/observ/simple_span_processor_test.go @@ -4,13 +4,14 @@ package observ_test import ( "errors" + "testing" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/sdk/trace/internal/observ" semconv "go.opentelemetry.io/otel/semconv/v1.37.0" - "testing" ) const sspComponentId = 0 From 8e9b90b07c133e44a98f260fb756f0da32d9cf7d Mon Sep 17 00:00:00 2001 From: Mahendra Bishnoi Date: Sat, 27 Sep 2025 13:25:04 +0530 Subject: [PATCH 18/30] add benchmark test --- .../observ/simple_span_processor_test.go | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/sdk/trace/internal/observ/simple_span_processor_test.go b/sdk/trace/internal/observ/simple_span_processor_test.go index e56590fca37..7105e7601ed 100644 --- a/sdk/trace/internal/observ/simple_span_processor_test.go +++ b/sdk/trace/internal/observ/simple_span_processor_test.go @@ -4,6 +4,7 @@ package observ_test import ( "errors" + "go.opentelemetry.io/otel/metric/noop" "testing" "github.com/stretchr/testify/assert" @@ -63,6 +64,57 @@ func TestSSPSpanProcessed(t *testing.T) { func BenchmarkSSP(b *testing.B) { b.Setenv("OTEL_GO_X_OBSERVABILITY", "true") + + newSSP := func(b *testing.B) *observ.SSP { + b.Helper() + ssp, err := observ.NewSSP(sspComponentId) + require.NoError(b, err) + require.NotNil(b, ssp) + return ssp + } + + b.Run("SpanProcessed", func(b *testing.B) { + orig := otel.GetMeterProvider() + b.Cleanup(func() { + otel.SetMeterProvider(orig) + }) + + // Ensure deterministic benchmark by using noop meter. + otel.SetMeterProvider(noop.NewMeterProvider()) + + ssp := newSSP(b) + ctx := b.Context() + + b.ResetTimer() + b.ReportAllocs() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + ssp.SpanProcessed(ctx, 10, nil) + } + }) + }) + + b.Run("SpanProcessedWithError", func(b *testing.B) { + orig := otel.GetMeterProvider() + b.Cleanup(func() { + otel.SetMeterProvider(orig) + }) + + // Ensure deterministic benchmark by using noop meter. + otel.SetMeterProvider(noop.NewMeterProvider()) + + ssp := newSSP(b) + ctx := b.Context() + processErr := errors.New("error processing span") + + b.ResetTimer() + b.ReportAllocs() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + ssp.SpanProcessed(ctx, 10, processErr) + } + }) + }) } func sspSet(attrs ...attribute.KeyValue) attribute.Set { From 1cca2be9a8d558c12d74a4d25ba4f4e4728db7f7 Mon Sep 17 00:00:00 2001 From: Mahendra Bishnoi Date: Sat, 27 Sep 2025 13:25:30 +0530 Subject: [PATCH 19/30] fix formatting --- sdk/trace/internal/observ/simple_span_processor_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/trace/internal/observ/simple_span_processor_test.go b/sdk/trace/internal/observ/simple_span_processor_test.go index 7105e7601ed..21c9ed648b5 100644 --- a/sdk/trace/internal/observ/simple_span_processor_test.go +++ b/sdk/trace/internal/observ/simple_span_processor_test.go @@ -4,13 +4,13 @@ package observ_test import ( "errors" - "go.opentelemetry.io/otel/metric/noop" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric/noop" "go.opentelemetry.io/otel/sdk/trace/internal/observ" semconv "go.opentelemetry.io/otel/semconv/v1.37.0" ) From 870ed151eee80a39c2f39aced6d76a3d60f24ba0 Mon Sep 17 00:00:00 2001 From: Mahendra Bishnoi Date: Sat, 27 Sep 2025 13:32:55 +0530 Subject: [PATCH 20/30] fix formatting --- sdk/trace/internal/observ/simple_span_processor_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/sdk/trace/internal/observ/simple_span_processor_test.go b/sdk/trace/internal/observ/simple_span_processor_test.go index 21c9ed648b5..d40aebfcdef 100644 --- a/sdk/trace/internal/observ/simple_span_processor_test.go +++ b/sdk/trace/internal/observ/simple_span_processor_test.go @@ -8,6 +8,7 @@ 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/noop" From 927d13b85b26b430fb44f1b72b3f3700ed6f70ca Mon Sep 17 00:00:00 2001 From: Mahendra Bishnoi Date: Sun, 28 Sep 2025 15:51:39 +0530 Subject: [PATCH 21/30] review comment - rename sspComponentId to sspComponentID --- .../internal/observ/simple_span_processor_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sdk/trace/internal/observ/simple_span_processor_test.go b/sdk/trace/internal/observ/simple_span_processor_test.go index d40aebfcdef..66bcd84e134 100644 --- a/sdk/trace/internal/observ/simple_span_processor_test.go +++ b/sdk/trace/internal/observ/simple_span_processor_test.go @@ -16,7 +16,7 @@ import ( semconv "go.opentelemetry.io/otel/semconv/v1.37.0" ) -const sspComponentId = 0 +const sspComponentID = 0 func TestSSPComponentName(t *testing.T) { got := observ.SSPComponentName(10) @@ -33,13 +33,13 @@ func TestNewSSPError(t *testing.T) { mp := &errMeterProvider{err: assert.AnError} otel.SetMeterProvider(mp) - _, err := observ.NewSSP(sspComponentId) + _, err := observ.NewSSP(sspComponentID) require.ErrorIs(t, err, assert.AnError, "new instrument errors") assert.ErrorContains(t, err, "create SSP processed spans metric") } func TestNewSSPDisabled(t *testing.T) { - ssp, err := observ.NewSSP(sspComponentId) + ssp, err := observ.NewSSP(sspComponentID) assert.NoError(t, err) assert.Nil(t, ssp) } @@ -47,7 +47,7 @@ func TestNewSSPDisabled(t *testing.T) { func TestSSPSpanProcessed(t *testing.T) { ctx := t.Context() collect := setup(t) - ssp, err := observ.NewSSP(sspComponentId) + ssp, err := observ.NewSSP(sspComponentID) assert.NoError(t, err) ssp.SpanProcessed(ctx, 1, nil) @@ -68,7 +68,7 @@ func BenchmarkSSP(b *testing.B) { newSSP := func(b *testing.B) *observ.SSP { b.Helper() - ssp, err := observ.NewSSP(sspComponentId) + ssp, err := observ.NewSSP(sspComponentID) require.NoError(b, err) require.NotNil(b, ssp) return ssp @@ -121,6 +121,6 @@ func BenchmarkSSP(b *testing.B) { func sspSet(attrs ...attribute.KeyValue) attribute.Set { return attribute.NewSet(append([]attribute.KeyValue{ semconv.OTelComponentTypeSimpleSpanProcessor, - observ.SSPComponentName(sspComponentId), + observ.SSPComponentName(sspComponentID), }, attrs...)...) } From 9c29553e8dedbb0cf2f0a4db14acd65504c5fe5f Mon Sep 17 00:00:00 2001 From: Mahendra Bishnoi Date: Wed, 8 Oct 2025 19:43:22 +0530 Subject: [PATCH 22/30] capture span processed metric irrespective of whether its sampled or not. --- sdk/trace/simple_span_processor.go | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/sdk/trace/simple_span_processor.go b/sdk/trace/simple_span_processor.go index 88ff6261017..69e71fbbff1 100644 --- a/sdk/trace/simple_span_processor.go +++ b/sdk/trace/simple_span_processor.go @@ -66,17 +66,19 @@ func (ssp *simpleSpanProcessor) OnEnd(s ReadOnlySpan) { ssp.exporterMu.Lock() defer ssp.exporterMu.Unlock() + var err error if ssp.exporter != nil && s.SpanContext().TraceFlags().IsSampled() { - err := ssp.exporter.ExportSpans(context.Background(), []ReadOnlySpan{s}) + err = ssp.exporter.ExportSpans(context.Background(), []ReadOnlySpan{s}) if err != nil { otel.Handle(err) } - if ssp.inst != nil { - // Add the span to the context to ensure the metric is recorded - // with the correct span context. - ctx := trace.ContextWithSpanContext(context.Background(), s.SpanContext()) - ssp.inst.SpanProcessed(ctx, 1, err) - } + } + + if ssp.inst != nil { + // Add the span to the context to ensure the metric is recorded + // with the correct span context. + ctx := trace.ContextWithSpanContext(context.Background(), s.SpanContext()) + ssp.inst.SpanProcessed(ctx, 1, err) } } From 83f6d2a17f705ffdc69988f2e0535bb94c618850 Mon Sep 17 00:00:00 2001 From: Mahendra Bishnoi Date: Sat, 11 Oct 2025 22:39:28 +0530 Subject: [PATCH 23/30] review comments, optimize memory allocation by precomputing in success case, add docs for public methods --- .../internal/observ/simple_span_processor.go | 41 +++++++++++++------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/sdk/trace/internal/observ/simple_span_processor.go b/sdk/trace/internal/observ/simple_span_processor.go index 81d2ab6f0a9..0a547715f54 100644 --- a/sdk/trace/internal/observ/simple_span_processor.go +++ b/sdk/trace/internal/observ/simple_span_processor.go @@ -28,9 +28,11 @@ var measureAttrsPool = sync.Pool{ }, } +// SSP is the instrumentation for an OTel SDK SimpleSpanProcessor. type SSP struct { - componentNameAttr attribute.KeyValue - spansProcessedCounter otelconv.SDKProcessorSpanProcessed + spansProcessedCounter metric.Int64Counter + addOpt metric.AddOption + attrs []attribute.KeyValue } // SSPComponentName returns the component name attribute for a @@ -41,6 +43,10 @@ func SSPComponentName(id int64) attribute.KeyValue { return semconv.OTelComponentName(name) } +// NewSSP returns instrumentation for an OTel SDK SimpleSpanProcessor with the +// provided ID. +// +// If the experimental observability is disabled, nil is returned. func NewSSP(id int64) (*SSP, error) { if !x.Observability.Enabled() { return nil, nil @@ -51,28 +57,39 @@ func NewSSP(id int64) (*SSP, error) { metric.WithInstrumentationVersion(sdk.Version()), metric.WithSchemaURL(SchemaURL), ) - componentName := SSPComponentName(id) spansProcessedCounter, err := otelconv.NewSDKProcessorSpanProcessed(meter) if err != nil { err = fmt.Errorf("failed to create SSP processed spans metric: %w", err) } + + componentName := SSPComponentName(id) + componentType := spansProcessedCounter.AttrComponentType(otelconv.ComponentTypeSimpleSpanProcessor) + attrs := []attribute.KeyValue{componentName, componentType} + addOpt := metric.WithAttributeSet(attribute.NewSet(attrs...)) + return &SSP{ - componentNameAttr: componentName, - spansProcessedCounter: spansProcessedCounter, + spansProcessedCounter: spansProcessedCounter.Int64Counter, + addOpt: addOpt, + attrs: attrs, }, err } func (ssp *SSP) SpanProcessed(ctx context.Context, count int64, err error) { + ssp.spansProcessedCounter.Add(ctx, count, ssp.addOption(err)) +} + +func (ssp *SSP) addOption(err error) metric.AddOption { + if err == nil { + return ssp.addOpt + } attrs := measureAttrsPool.Get().(*[]attribute.KeyValue) defer func() { *attrs = (*attrs)[:0] // reset the slice for reuse measureAttrsPool.Put(attrs) }() - if err != nil { - *attrs = append(*attrs, semconv.ErrorType(err)) - } - *attrs = append(*attrs, - ssp.componentNameAttr, - ssp.spansProcessedCounter.AttrComponentType(otelconv.ComponentTypeSimpleSpanProcessor)) - ssp.spansProcessedCounter.Add(ctx, count, *attrs...) + *attrs = append(*attrs, ssp.attrs...) + *attrs = append(*attrs, semconv.ErrorType(err)) + // Do not inefficiently make a copy of attrs by using + // WithAttributes instead of WithAttributeSet. + return metric.WithAttributeSet(attribute.NewSet(*attrs...)) } From 1028666159ec2e87c928b5d988e2ab4af072bcb3 Mon Sep 17 00:00:00 2001 From: Mahendra Bishnoi Date: Sat, 11 Oct 2025 22:41:21 +0530 Subject: [PATCH 24/30] rename self observability -> observability --- sdk/trace/simple_span_processor_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/trace/simple_span_processor_test.go b/sdk/trace/simple_span_processor_test.go index 098ea5cca28..be01ec2fc14 100644 --- a/sdk/trace/simple_span_processor_test.go +++ b/sdk/trace/simple_span_processor_test.go @@ -192,7 +192,7 @@ func TestSimpleSpanProcessorShutdownHonorsContextCancel(t *testing.T) { } } -func TestSimpleSpanProcessorSelfObservability(t *testing.T) { +func TestSimpleSpanProcessorObservability(t *testing.T) { tests := []struct { name string enabled bool @@ -304,7 +304,7 @@ func TestSimpleSpanProcessorSelfObservability(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - t.Setenv("OTEL_GO_X_SELF_OBSERVABILITY", strconv.FormatBool(test.enabled)) + t.Setenv("OTEL_GO_X_OBSERVABILITY", strconv.FormatBool(test.enabled)) original := otel.GetMeterProvider() t.Cleanup(func() { otel.SetMeterProvider(original) }) From 7f1ab1be4f126fb09ef2284883caade042327033 Mon Sep 17 00:00:00 2001 From: Mahendra Bishnoi Date: Sat, 11 Oct 2025 22:49:09 +0530 Subject: [PATCH 25/30] fix CHANGELOG.md --- CHANGELOG.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f879ab0eab1..a05c8e3dae9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,8 +19,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Add experimental observability metrics in `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc`. (#7353) - Add experimental observability metrics in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc`. (#7459) - Add experimental observability metrics in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp`. (#7486) -- Add experimental observability for simple span processor metrics in `go.opentelemetry.io/otel/sdk/trace`. - Check the `go.opentelemetry.io/otel/sdk/trace/internal/x` package documentation for more information. (#7374) +- Add experimental observability metrics for simple span processor in `go.opentelemetry.io/otel/sdk/trace`. (#7374) ### Fixed From 7ae87cba968a9aa67d45c4eaacdea8a42fdbd000 Mon Sep 17 00:00:00 2001 From: Mahendra Bishnoi Date: Sat, 11 Oct 2025 23:06:25 +0530 Subject: [PATCH 26/30] retrigger build From c44abb69cd69a54324022e8886ffca0b3d271398 Mon Sep 17 00:00:00 2001 From: Mahendra Bishnoi Date: Sat, 11 Oct 2025 23:21:47 +0530 Subject: [PATCH 27/30] reduce allocations in happy path to 0 (from 1) --- sdk/trace/internal/observ/simple_span_processor.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sdk/trace/internal/observ/simple_span_processor.go b/sdk/trace/internal/observ/simple_span_processor.go index 0a547715f54..6af1018ab86 100644 --- a/sdk/trace/internal/observ/simple_span_processor.go +++ b/sdk/trace/internal/observ/simple_span_processor.go @@ -31,7 +31,7 @@ var measureAttrsPool = sync.Pool{ // SSP is the instrumentation for an OTel SDK SimpleSpanProcessor. type SSP struct { spansProcessedCounter metric.Int64Counter - addOpt metric.AddOption + addOpts []metric.AddOption attrs []attribute.KeyValue } @@ -65,22 +65,22 @@ func NewSSP(id int64) (*SSP, error) { componentName := SSPComponentName(id) componentType := spansProcessedCounter.AttrComponentType(otelconv.ComponentTypeSimpleSpanProcessor) attrs := []attribute.KeyValue{componentName, componentType} - addOpt := metric.WithAttributeSet(attribute.NewSet(attrs...)) + addOpts := []metric.AddOption{metric.WithAttributeSet(attribute.NewSet(attrs...))} return &SSP{ spansProcessedCounter: spansProcessedCounter.Int64Counter, - addOpt: addOpt, + addOpts: addOpts, attrs: attrs, }, err } func (ssp *SSP) SpanProcessed(ctx context.Context, count int64, err error) { - ssp.spansProcessedCounter.Add(ctx, count, ssp.addOption(err)) + ssp.spansProcessedCounter.Add(ctx, count, ssp.addOption(err)...) } -func (ssp *SSP) addOption(err error) metric.AddOption { +func (ssp *SSP) addOption(err error) []metric.AddOption { if err == nil { - return ssp.addOpt + return ssp.addOpts } attrs := measureAttrsPool.Get().(*[]attribute.KeyValue) defer func() { @@ -91,5 +91,5 @@ func (ssp *SSP) addOption(err error) metric.AddOption { *attrs = append(*attrs, semconv.ErrorType(err)) // Do not inefficiently make a copy of attrs by using // WithAttributes instead of WithAttributeSet. - return metric.WithAttributeSet(attribute.NewSet(*attrs...)) + return []metric.AddOption{metric.WithAttributeSet(attribute.NewSet(*attrs...))} } From 747e794b01159e571e1c8a0ca8c66341a8df2a63 Mon Sep 17 00:00:00 2001 From: Mahendra Bishnoi Date: Tue, 14 Oct 2025 13:48:58 +0530 Subject: [PATCH 28/30] address review comments --- sdk/trace/internal/observ/simple_span_processor.go | 8 +++++--- .../internal/observ/simple_span_processor_test.go | 11 ++++++----- sdk/trace/simple_span_processor.go | 2 +- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/sdk/trace/internal/observ/simple_span_processor.go b/sdk/trace/internal/observ/simple_span_processor.go index 6af1018ab86..cfa21e229fa 100644 --- a/sdk/trace/internal/observ/simple_span_processor.go +++ b/sdk/trace/internal/observ/simple_span_processor.go @@ -68,14 +68,16 @@ func NewSSP(id int64) (*SSP, error) { addOpts := []metric.AddOption{metric.WithAttributeSet(attribute.NewSet(attrs...))} return &SSP{ - spansProcessedCounter: spansProcessedCounter.Int64Counter, + spansProcessedCounter: spansProcessedCounter.Inst(), addOpts: addOpts, attrs: attrs, }, err } -func (ssp *SSP) SpanProcessed(ctx context.Context, count int64, err error) { - ssp.spansProcessedCounter.Add(ctx, count, ssp.addOption(err)...) +// SpanProcessed records that a span has been processed by the SimpleSpanProcessor. +// If err is non-nil, it records the processing error as an attribute. +func (ssp *SSP) SpanProcessed(ctx context.Context, err error) { + ssp.spansProcessedCounter.Add(ctx, 1, ssp.addOption(err)...) } func (ssp *SSP) addOption(err error) []metric.AddOption { diff --git a/sdk/trace/internal/observ/simple_span_processor_test.go b/sdk/trace/internal/observ/simple_span_processor_test.go index 66bcd84e134..a6b0e359e22 100644 --- a/sdk/trace/internal/observ/simple_span_processor_test.go +++ b/sdk/trace/internal/observ/simple_span_processor_test.go @@ -50,13 +50,14 @@ func TestSSPSpanProcessed(t *testing.T) { ssp, err := observ.NewSSP(sspComponentID) assert.NoError(t, err) - ssp.SpanProcessed(ctx, 1, nil) + ssp.SpanProcessed(ctx, nil) check(t, collect(), processed(dPt(sspSet(), 1))) - ssp.SpanProcessed(ctx, 2, nil) + ssp.SpanProcessed(ctx, nil) + ssp.SpanProcessed(ctx, nil) check(t, collect(), processed(dPt(sspSet(), 3))) processErr := errors.New("error processing span") - ssp.SpanProcessed(ctx, 1, processErr) + ssp.SpanProcessed(ctx, processErr) check(t, collect(), processed( dPt(sspSet(), 3), dPt(sspSet(semconv.ErrorType(processErr)), 1), @@ -90,7 +91,7 @@ func BenchmarkSSP(b *testing.B) { b.ReportAllocs() b.RunParallel(func(pb *testing.PB) { for pb.Next() { - ssp.SpanProcessed(ctx, 10, nil) + ssp.SpanProcessed(ctx, nil) } }) }) @@ -112,7 +113,7 @@ func BenchmarkSSP(b *testing.B) { b.ReportAllocs() b.RunParallel(func(pb *testing.PB) { for pb.Next() { - ssp.SpanProcessed(ctx, 10, processErr) + ssp.SpanProcessed(ctx, processErr) } }) }) diff --git a/sdk/trace/simple_span_processor.go b/sdk/trace/simple_span_processor.go index 69e71fbbff1..771e427a4c5 100644 --- a/sdk/trace/simple_span_processor.go +++ b/sdk/trace/simple_span_processor.go @@ -78,7 +78,7 @@ func (ssp *simpleSpanProcessor) OnEnd(s ReadOnlySpan) { // Add the span to the context to ensure the metric is recorded // with the correct span context. ctx := trace.ContextWithSpanContext(context.Background(), s.SpanContext()) - ssp.inst.SpanProcessed(ctx, 1, err) + ssp.inst.SpanProcessed(ctx, err) } } From c08bb45042d1c6b82851b51ca6a249f28a5c0bf1 Mon Sep 17 00:00:00 2001 From: Mahendra Bishnoi Date: Fri, 17 Oct 2025 20:27:31 +0530 Subject: [PATCH 29/30] remove file --- sdk/trace/internal/x/README.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 sdk/trace/internal/x/README.md diff --git a/sdk/trace/internal/x/README.md b/sdk/trace/internal/x/README.md deleted file mode 100644 index e69de29bb2d..00000000000 From 69b3a1401bbcdb62cf78011f7d665f399f7c4fca Mon Sep 17 00:00:00 2001 From: Mahendra Bishnoi Date: Fri, 17 Oct 2025 20:36:00 +0530 Subject: [PATCH 30/30] fix import --- sdk/trace/internal/observ/simple_span_processor.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/trace/internal/observ/simple_span_processor.go b/sdk/trace/internal/observ/simple_span_processor.go index cfa21e229fa..7d33870613a 100644 --- a/sdk/trace/internal/observ/simple_span_processor.go +++ b/sdk/trace/internal/observ/simple_span_processor.go @@ -12,7 +12,7 @@ import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/sdk" - "go.opentelemetry.io/otel/sdk/trace/internal/x" + "go.opentelemetry.io/otel/sdk/internal/x" semconv "go.opentelemetry.io/otel/semconv/v1.37.0" "go.opentelemetry.io/otel/semconv/v1.37.0/otelconv" )