Skip to content

Commit 08322fd

Browse files
shayannabxrmx
andauthored
fix(sdk): add max_spans limit to InMemorySpanExporter (#5296)
* fix(sdk): add max_spans limit to InMemorySpanExporter * test(sdk): improve test coverage and modernize type hints in InMemorySpanExporter - Update InMemorySpanExporter type hint from typing.Optional[int] to int | None. - Refactor test_max_spans_limit to test distinct spans and assert order. * Apply suggestion from @xrmx * Create 5296.added * Rename 5296.added to .changelog/5296.added --------- Co-authored-by: Riccardo Magliocchetti <riccardo.magliocchetti@gmail.com>
1 parent c02b423 commit 08322fd

3 files changed

Lines changed: 29 additions & 3 deletions

File tree

.changelog/5296.added

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
`opentelemetry-sdk`: Make it possible to limit the size of stored spans in InMemorySpanExporter

opentelemetry-sdk/src/opentelemetry/sdk/trace/export/in_memory_span_exporter.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import collections.abc
55
import threading
6+
from collections import deque
67

78
from opentelemetry.sdk.trace import ReadableSpan
89
from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult
@@ -12,12 +13,17 @@ class InMemorySpanExporter(SpanExporter):
1213
"""Implementation of :class:`.SpanExporter` that stores spans in memory.
1314
1415
This class can be used for testing purposes. It stores the exported spans
15-
in a list in memory that can be retrieved using the
16+
in a deque in memory that can be retrieved using the
1617
:func:`.get_finished_spans` method.
18+
19+
Args:
20+
max_spans: Maximum number of spans to store in memory. When the limit
21+
is reached, the oldest spans are dropped automatically. If ``None``
22+
(default), there is no limit.
1723
"""
1824

19-
def __init__(self) -> None:
20-
self._finished_spans: list[ReadableSpan] = []
25+
def __init__(self, max_spans: int | None = None) -> None:
26+
self._finished_spans: deque[ReadableSpan] = deque(maxlen=max_spans)
2127
self._stopped = False
2228
self._lock = threading.Lock()
2329

opentelemetry-sdk/tests/trace/export/test_in_memory_span_exporter.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,22 @@ def test_return_code(self):
6262
# after shutdown export should fail
6363
ret = memory_exporter.export(span_list)
6464
self.assertEqual(ret, export.SpanExportResult.FAILURE)
65+
66+
def test_max_spans_limit(self):
67+
"""Test that max_spans limits memory usage by dropping oldest spans."""
68+
exporter = InMemorySpanExporter(max_spans=2)
69+
span1 = trace._Span("span1", mock.Mock(spec=trace_api.SpanContext))
70+
span2 = trace._Span("span2", mock.Mock(spec=trace_api.SpanContext))
71+
span3 = trace._Span("span3", mock.Mock(spec=trace_api.SpanContext))
72+
exporter.export([span1, span2, span3])
73+
span_list = exporter.get_finished_spans()
74+
self.assertEqual(len(span_list), 2)
75+
self.assertSequenceEqual((span2, span3), span_list)
76+
77+
def test_max_spans_none_is_unlimited(self):
78+
"""Test that max_spans=None (default) imposes no limit."""
79+
exporter = InMemorySpanExporter(max_spans=None)
80+
span = trace._Span("name", mock.Mock(spec=trace_api.SpanContext))
81+
exporter.export([span] * 100)
82+
span_list = exporter.get_finished_spans()
83+
self.assertEqual(len(span_list), 100)

0 commit comments

Comments
 (0)