Skip to content

Commit

Permalink
sdk/trace/exporter: add InMemorySpanExporter
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
mauriciovasquezbernal committed Sep 2, 2019
1 parent c678c41 commit fc0bfd9
Show file tree
Hide file tree
Showing 2 changed files with 155 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Copyright 2019, OpenTelemetry Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import threading
import typing

from .. import Span
from . import Exporter, ExportResult


class InMemorySpanExporter(Exporter):
"""Implementation of :class:`.Exporter` that stores spans in memory.
This class can be used for testing purposes. It stores the exported spans
in a list in memory that can be retrieve using the
:func:`.get_finished_spans` method
"""

def __init__(self):
self._finished_spans = []
self._stopped = False
self._lock = threading.Lock()

def clear(self):
"""Clear list of collected spans."""
with self._lock:
self._finished_spans.clear()

def get_finished_spans(self):
"""Get list of collected spans."""
with self._lock:
return tuple(self._finished_spans)

def export(self, spans: typing.Sequence[Span]) -> ExportResult:
"""Stores a list of spans in memory."""
if self._stopped:
return ExportResult.FAILED_NONE_RETRYABLE
with self._lock:
self._finished_spans.extend(spans)
return ExportResult.SUCCESS

def shutdown(self):
"""Shut downs the exporter.
Calls to export after the exporter has been shut down will fail.
"""
self._stopped = True
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# Copyright 2019, OpenTelemetry Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import unittest
from unittest import mock

from opentelemetry import trace as trace_api
from opentelemetry.sdk import trace
from opentelemetry.sdk.trace import export
from opentelemetry.sdk.trace.export.in_memory_span_exporter import (
InMemorySpanExporter,
)


class TestInMemorySpanExporter(unittest.TestCase):
def test_get_finished_spans(self):
tracer = trace.Tracer()

memory_exporter = InMemorySpanExporter()
span_processor = export.SimpleSpanProcessor(memory_exporter)
tracer.add_span_processor(span_processor)

with tracer.start_span("foo"):
with tracer.start_span("bar"):
with tracer.start_span("xxx"):
pass

span_list = memory_exporter.get_finished_spans()
spans_names_list = [span.name for span in span_list]
self.assertListEqual(["xxx", "bar", "foo"], spans_names_list)

def test_clear(self):
tracer = trace.Tracer()

memory_exporter = InMemorySpanExporter()
span_processor = export.SimpleSpanProcessor(memory_exporter)
tracer.add_span_processor(span_processor)

with tracer.start_span("foo"):
with tracer.start_span("bar"):
with tracer.start_span("xxx"):
pass

memory_exporter.clear()
span_list = memory_exporter.get_finished_spans()
self.assertEqual(len(span_list), 0)

def test_shutdown(self):
tracer = trace.Tracer()

memory_exporter = InMemorySpanExporter()
span_processor = export.SimpleSpanProcessor(memory_exporter)
tracer.add_span_processor(span_processor)

with tracer.start_span("foo"):
with tracer.start_span("bar"):
with tracer.start_span("xxx"):
pass

span_list = memory_exporter.get_finished_spans()
self.assertEqual(len(span_list), 3)

memory_exporter.shutdown()

# after shutdown no new spans are accepted
with tracer.start_span("foo"):
with tracer.start_span("bar"):
with tracer.start_span("xxx"):
pass

span_list = memory_exporter.get_finished_spans()
self.assertEqual(len(span_list), 3)

def test_return_code(self):
span = trace.Span("name", mock.Mock(spec=trace_api.SpanContext))
span_list = (span,)
memory_exporter = InMemorySpanExporter()

ret = memory_exporter.export(span_list)
self.assertEqual(ret, export.ExportResult.SUCCESS)

memory_exporter.shutdown()

# after shutdown export should fail
ret = memory_exporter.export(span_list)
self.assertEqual(ret, export.ExportResult.FAILED_NONE_RETRYABLE)

0 comments on commit fc0bfd9

Please sign in to comment.