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 5, 2019
1 parent a599ef0 commit e864c58
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ def on_start(self, span: Span) -> None:
def on_end(self, span: Span) -> None:
try:
self.span_exporter.export((span,))
# pylint: disable=broad-except
except Exception as exc:
logging.warning("Exception while exporting data: %s", exc)

Expand Down
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 SpanExporter, SpanExportResult


class InMemorySpanExporter(SpanExporter):
"""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]) -> SpanExportResult:
"""Stores a list of spans in memory."""
if self._stopped:
return SpanExportResult.FAILED_NOT_RETRYABLE
with self._lock:
self._finished_spans.extend(spans)
return SpanExportResult.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.SimpleExportSpanProcessor(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.SimpleExportSpanProcessor(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.SimpleExportSpanProcessor(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.SpanExportResult.SUCCESS)

memory_exporter.shutdown()

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

0 comments on commit e864c58

Please sign in to comment.