You've already forked opentelemetry-go
mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2025-09-16 09:26:25 +02:00
Pool attribute slices in stdouttrace
self-observability (#7201)
### Benchmarks ```terminal goos: linux goarch: amd64 pkg: go.opentelemetry.io/otel/exporters/stdout/stdouttrace cpu: Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz │ main.txt │ stdouttrace-pool-obs-attrs.txt │ │ sec/op │ sec/op vs base │ ExporterExportSpans-8 30.55µ ± 2% 31.76µ ± 3% +3.96% (p=0.000 n=10) │ main.txt │ stdouttrace-pool-obs-attrs.txt │ │ B/op │ B/op vs base │ ExporterExportSpans-8 6.538Ki ± 0% 6.353Ki ± 0% -2.84% (p=0.000 n=10) │ main.txt │ stdouttrace-pool-obs-attrs.txt │ │ allocs/op │ allocs/op vs base │ ExporterExportSpans-8 135.0 ± 0% 134.0 ± 0% -0.74% (p=0.000 n=10) ```
This commit is contained in:
@@ -96,6 +96,17 @@ type Exporter struct {
|
||||
operationDurationMetric otelconv.SDKExporterOperationDuration
|
||||
}
|
||||
|
||||
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
|
||||
},
|
||||
}
|
||||
|
||||
// ExportSpans writes spans in json format to stdout.
|
||||
func (e *Exporter) ExportSpans(ctx context.Context, spans []trace.ReadOnlySpan) (err error) {
|
||||
var success int64
|
||||
@@ -116,11 +127,14 @@ func (e *Exporter) ExportSpans(ctx context.Context, spans []trace.ReadOnlySpan)
|
||||
if err != nil {
|
||||
// additional attributes for self-observability,
|
||||
// only spanExportedMetric and operationDurationMetric are supported.
|
||||
//
|
||||
// TODO: use a pool to amortize allocations.
|
||||
attr = make([]attribute.KeyValue, len(e.selfObservabilityAttrs), len(e.selfObservabilityAttrs)+1)
|
||||
copy(attr, e.selfObservabilityAttrs)
|
||||
attr = append(attr, semconv.ErrorType(err))
|
||||
attrs := measureAttrsPool.Get().(*[]attribute.KeyValue)
|
||||
defer func() {
|
||||
*attrs = (*attrs)[:0] // reset the slice for reuse
|
||||
measureAttrsPool.Put(attrs)
|
||||
}()
|
||||
*attrs = append(*attrs, e.selfObservabilityAttrs...)
|
||||
*attrs = append(*attrs, semconv.ErrorType(err))
|
||||
attr = *attrs
|
||||
|
||||
e.spanExportedMetric.Add(ctx, count-success, attr...)
|
||||
}
|
||||
|
@@ -667,3 +667,26 @@ func TestSelfObservabilityInstrumentErrors(t *testing.T) {
|
||||
assert.ErrorContains(t, err, "span exported metric")
|
||||
assert.ErrorContains(t, err, "operation duration metric")
|
||||
}
|
||||
|
||||
func BenchmarkExporterExportSpans(b *testing.B) {
|
||||
b.Setenv("OTEL_GO_X_SELF_OBSERVABILITY", "true")
|
||||
ss := tracetest.SpanStubs{
|
||||
{Name: "/foo"},
|
||||
{
|
||||
Name: "JSON encoder cannot marshal math.Inf(1)",
|
||||
Attributes: []attribute.KeyValue{attribute.Float64("", math.Inf(1))},
|
||||
},
|
||||
{Name: "/bar"},
|
||||
}.Snapshots()
|
||||
ex, err := stdouttrace.New(stdouttrace.WithWriter(io.Discard))
|
||||
if err != nil {
|
||||
b.Fatalf("failed to create exporter: %v", err)
|
||||
}
|
||||
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
err = ex.ExportSpans(context.Background(), ss)
|
||||
}
|
||||
_ = err
|
||||
}
|
||||
|
Reference in New Issue
Block a user