From c11f724e2f6aedc2017556f2966f441a15eb4578 Mon Sep 17 00:00:00 2001 From: shayannab Date: Fri, 12 Jun 2026 13:18:36 +0530 Subject: [PATCH] fix(sdk): add max_spans limit to InMemorySpanExporter --- .../sdk/trace/export/in_memory_span_exporter.py | 12 +++++++++--- .../trace/export/test_in_memory_span_exporter.py | 15 +++++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/trace/export/in_memory_span_exporter.py b/opentelemetry-sdk/src/opentelemetry/sdk/trace/export/in_memory_span_exporter.py index 25a66294659..508bb46f6be 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/trace/export/in_memory_span_exporter.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/trace/export/in_memory_span_exporter.py @@ -3,6 +3,7 @@ import threading import typing +from collections import deque from opentelemetry.sdk.trace import ReadableSpan from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult @@ -14,10 +15,15 @@ class InMemorySpanExporter(SpanExporter): This class can be used for testing purposes. It stores the exported spans in a list in memory that can be retrieved using the :func:`.get_finished_spans` method. + + Args: + max_spans: Maximum number of spans to store in memory. When the limit + is reached, the oldest spans are dropped automatically. If ``None`` + (default), there is no limit. """ - def __init__(self) -> None: - self._finished_spans: list[ReadableSpan] = [] + def __init__(self, max_spans: typing.Optional[int] = None) -> None: + self._finished_spans: deque[ReadableSpan] = deque(maxlen=max_spans) self._stopped = False self._lock = threading.Lock() @@ -47,4 +53,4 @@ def shutdown(self) -> None: self._stopped = True def force_flush(self, timeout_millis: int = 30000) -> bool: - return True + return True \ No newline at end of file diff --git a/opentelemetry-sdk/tests/trace/export/test_in_memory_span_exporter.py b/opentelemetry-sdk/tests/trace/export/test_in_memory_span_exporter.py index 7f30f70dd97..258d3d8389c 100644 --- a/opentelemetry-sdk/tests/trace/export/test_in_memory_span_exporter.py +++ b/opentelemetry-sdk/tests/trace/export/test_in_memory_span_exporter.py @@ -62,3 +62,18 @@ def test_return_code(self): # after shutdown export should fail ret = memory_exporter.export(span_list) self.assertEqual(ret, export.SpanExportResult.FAILURE) + def test_max_spans_limit(self): + """Test that max_spans limits memory usage by dropping oldest spans.""" + exporter = InMemorySpanExporter(max_spans=2) + span = trace._Span("name", mock.Mock(spec=trace_api.SpanContext)) + exporter.export([span, span, span]) + span_list = exporter.get_finished_spans() + self.assertEqual(len(span_list), 2) + + def test_max_spans_none_is_unlimited(self): + """Test that max_spans=None (default) imposes no limit.""" + exporter = InMemorySpanExporter(max_spans=None) + span = trace._Span("name", mock.Mock(spec=trace_api.SpanContext)) + exporter.export([span] * 100) + span_list = exporter.get_finished_spans() + self.assertEqual(len(span_list), 100) \ No newline at end of file