diff --git a/CHANGELOG.md b/CHANGELOG.md index f0b871ebc06..68a8a9d7f3a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - `FilterProcessor.Enabled` in `go.opentelemetry.io/otel/sdk/log/internal/x` now accepts `EnabledParameters` instead of `Record`. (#5791) - The `Record` type in `go.opentelemetry.io/otel/log` is no longer comparable. (#5847) - Performance improvements for the trace SDK `SetAttributes` method in `Span`. (#5864) +- Reduce memory allocations for the `Event` and `Link` lists in `Span`. (#5858) ### Deprecated diff --git a/sdk/trace/evictedqueue.go b/sdk/trace/evictedqueue.go index 821c83faa1d..8c308dd60a9 100644 --- a/sdk/trace/evictedqueue.go +++ b/sdk/trace/evictedqueue.go @@ -12,25 +12,26 @@ import ( // evictedQueue is a FIFO queue with a configurable capacity. type evictedQueue[T any] struct { - queue []T - capacity int - droppedCount int - logDropped func() + queue []T + capacity int + droppedCount int + logDroppedMsg string + logDroppedOnce sync.Once } func newEvictedQueueEvent(capacity int) evictedQueue[Event] { // Do not pre-allocate queue, do this lazily. return evictedQueue[Event]{ - capacity: capacity, - logDropped: sync.OnceFunc(func() { global.Warn("limit reached: dropping trace trace.Event") }), + capacity: capacity, + logDroppedMsg: "limit reached: dropping trace trace.Event", } } func newEvictedQueueLink(capacity int) evictedQueue[Link] { // Do not pre-allocate queue, do this lazily. return evictedQueue[Link]{ - capacity: capacity, - logDropped: sync.OnceFunc(func() { global.Warn("limit reached: dropping trace trace.Link") }), + capacity: capacity, + logDroppedMsg: "limit reached: dropping trace trace.Link", } } @@ -53,6 +54,10 @@ func (eq *evictedQueue[T]) add(value T) { eq.queue = append(eq.queue, value) } +func (eq *evictedQueue[T]) logDropped() { + eq.logDroppedOnce.Do(func() { global.Warn(eq.logDroppedMsg) }) +} + // copy returns a copy of the evictedQueue. func (eq *evictedQueue[T]) copy() []T { return slices.Clone(eq.queue) diff --git a/sdk/trace/evictedqueue_test.go b/sdk/trace/evictedqueue_test.go index 7b88d63d077..66269395817 100644 --- a/sdk/trace/evictedqueue_test.go +++ b/sdk/trace/evictedqueue_test.go @@ -7,7 +7,11 @@ import ( "reflect" "testing" + "github.com/go-logr/logr" + "github.com/go-logr/logr/funcr" "github.com/stretchr/testify/assert" + + "go.opentelemetry.io/otel/internal/global" ) func init() { @@ -36,18 +40,25 @@ func TestCopy(t *testing.T) { func TestDropCount(t *testing.T) { q := newEvictedQueueEvent(3) - var called bool - q.logDropped = func() { called = true } + + var called int + t.Cleanup(func(l logr.Logger) func() { + return func() { global.SetLogger(l) } + }(global.GetLogger())) + global.SetLogger(funcr.New(func(prefix, args string) { + called++ + }, funcr.Options{Verbosity: 1})) q.add(Event{Name: "value1"}) - assert.False(t, called, `"value1" logged as dropped`) + assert.Equal(t, 0, called, `"value1" logged as dropped`) q.add(Event{Name: "value2"}) - assert.False(t, called, `"value2" logged as dropped`) + assert.Equal(t, 0, called, `"value2" logged as dropped`) q.add(Event{Name: "value3"}) - assert.False(t, called, `"value3" logged as dropped`) + assert.Equal(t, 0, called, `"value3" logged as dropped`) q.add(Event{Name: "value1"}) - assert.True(t, called, `"value2" not logged as dropped`) + assert.Equal(t, 1, called, `"value2" not logged as dropped`) q.add(Event{Name: "value4"}) + assert.Equal(t, 1, called, `"value4" logged as dropped`) if wantLen, gotLen := 3, len(q.queue); wantLen != gotLen { t.Errorf("got queue length %d want %d", gotLen, wantLen) }