Skip to content

Commit fc0bfd9

Browse files
sdk/trace/exporter: add InMemorySpanExporter
InMemorySpanExporter is a simple implementation of the Exporter interface that saves the exported spans in a list in memory. This class is useful for testing purposes.
1 parent c678c41 commit fc0bfd9

File tree

2 files changed

+155
-0
lines changed

2 files changed

+155
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Copyright 2019, OpenTelemetry Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import threading
16+
import typing
17+
18+
from .. import Span
19+
from . import Exporter, ExportResult
20+
21+
22+
class InMemorySpanExporter(Exporter):
23+
"""Implementation of :class:`.Exporter` that stores spans in memory.
24+
25+
This class can be used for testing purposes. It stores the exported spans
26+
in a list in memory that can be retrieve using the
27+
:func:`.get_finished_spans` method
28+
"""
29+
30+
def __init__(self):
31+
self._finished_spans = []
32+
self._stopped = False
33+
self._lock = threading.Lock()
34+
35+
def clear(self):
36+
"""Clear list of collected spans."""
37+
with self._lock:
38+
self._finished_spans.clear()
39+
40+
def get_finished_spans(self):
41+
"""Get list of collected spans."""
42+
with self._lock:
43+
return tuple(self._finished_spans)
44+
45+
def export(self, spans: typing.Sequence[Span]) -> ExportResult:
46+
"""Stores a list of spans in memory."""
47+
if self._stopped:
48+
return ExportResult.FAILED_NONE_RETRYABLE
49+
with self._lock:
50+
self._finished_spans.extend(spans)
51+
return ExportResult.SUCCESS
52+
53+
def shutdown(self):
54+
"""Shut downs the exporter.
55+
56+
Calls to export after the exporter has been shut down will fail.
57+
"""
58+
self._stopped = True
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# Copyright 2019, OpenTelemetry Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import unittest
16+
from unittest import mock
17+
18+
from opentelemetry import trace as trace_api
19+
from opentelemetry.sdk import trace
20+
from opentelemetry.sdk.trace import export
21+
from opentelemetry.sdk.trace.export.in_memory_span_exporter import (
22+
InMemorySpanExporter,
23+
)
24+
25+
26+
class TestInMemorySpanExporter(unittest.TestCase):
27+
def test_get_finished_spans(self):
28+
tracer = trace.Tracer()
29+
30+
memory_exporter = InMemorySpanExporter()
31+
span_processor = export.SimpleSpanProcessor(memory_exporter)
32+
tracer.add_span_processor(span_processor)
33+
34+
with tracer.start_span("foo"):
35+
with tracer.start_span("bar"):
36+
with tracer.start_span("xxx"):
37+
pass
38+
39+
span_list = memory_exporter.get_finished_spans()
40+
spans_names_list = [span.name for span in span_list]
41+
self.assertListEqual(["xxx", "bar", "foo"], spans_names_list)
42+
43+
def test_clear(self):
44+
tracer = trace.Tracer()
45+
46+
memory_exporter = InMemorySpanExporter()
47+
span_processor = export.SimpleSpanProcessor(memory_exporter)
48+
tracer.add_span_processor(span_processor)
49+
50+
with tracer.start_span("foo"):
51+
with tracer.start_span("bar"):
52+
with tracer.start_span("xxx"):
53+
pass
54+
55+
memory_exporter.clear()
56+
span_list = memory_exporter.get_finished_spans()
57+
self.assertEqual(len(span_list), 0)
58+
59+
def test_shutdown(self):
60+
tracer = trace.Tracer()
61+
62+
memory_exporter = InMemorySpanExporter()
63+
span_processor = export.SimpleSpanProcessor(memory_exporter)
64+
tracer.add_span_processor(span_processor)
65+
66+
with tracer.start_span("foo"):
67+
with tracer.start_span("bar"):
68+
with tracer.start_span("xxx"):
69+
pass
70+
71+
span_list = memory_exporter.get_finished_spans()
72+
self.assertEqual(len(span_list), 3)
73+
74+
memory_exporter.shutdown()
75+
76+
# after shutdown no new spans are accepted
77+
with tracer.start_span("foo"):
78+
with tracer.start_span("bar"):
79+
with tracer.start_span("xxx"):
80+
pass
81+
82+
span_list = memory_exporter.get_finished_spans()
83+
self.assertEqual(len(span_list), 3)
84+
85+
def test_return_code(self):
86+
span = trace.Span("name", mock.Mock(spec=trace_api.SpanContext))
87+
span_list = (span,)
88+
memory_exporter = InMemorySpanExporter()
89+
90+
ret = memory_exporter.export(span_list)
91+
self.assertEqual(ret, export.ExportResult.SUCCESS)
92+
93+
memory_exporter.shutdown()
94+
95+
# after shutdown export should fail
96+
ret = memory_exporter.export(span_list)
97+
self.assertEqual(ret, export.ExportResult.FAILED_NONE_RETRYABLE)

0 commit comments

Comments
 (0)