Skip to content

Commit acb7f48

Browse files
toumorokoshimauriciovasquezbernalalrex
authored
opentracing-shim: add testbed for otshim (open-telemetry#727)
This commit ports the OpenTracing testbed[1] to check that the ot-shim is working as expected using different frameworks. Gevent doesn't support context vars yet[2], so those tests are not compatible with opentelemetry and were not ported. [1] https://github.com/opentracing/opentracing-python/tree/master/testbed [2] gevent/gevent#1407 Co-authored-by: Mauricio Vásquez <mauricio@kinvolk.io> Co-authored-by: alrex <aboten@lightstep.com>
1 parent 3ad6ac5 commit acb7f48

File tree

43 files changed

+1506
-9
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1506
-9
lines changed

dev-requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ pytest!=5.2.3
1010
pytest-cov>=2.8
1111
readme-renderer~=24.0
1212
httpretty~=1.0
13+
opentracing~=2.2.0
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
2+
Testbed suite for the OpenTelemetry-OpenTracing Bridge
3+
======================================================
4+
5+
Testbed suite designed to test the API changes.
6+
7+
Build and test.
8+
---------------
9+
10+
.. code-block:: sh
11+
12+
tox -e py37-test-opentracing-shim
13+
14+
Alternatively, due to the organization of the suite, it's possible to run directly the tests using ``py.test``\ :
15+
16+
.. code-block:: sh
17+
18+
py.test -s testbed/test_multiple_callbacks/test_threads.py
19+
20+
Tested frameworks
21+
-----------------
22+
23+
Currently the examples cover ``threading`` and ``asyncio``.
24+
25+
List of patterns
26+
----------------
27+
28+
29+
* `Active Span replacement <test_active_span_replacement>`_ - Start an isolated task and query for its results in another task/thread.
30+
* `Client-Server <test_client_server>`_ - Typical client-server example.
31+
* `Common Request Handler <test_common_request_handler>`_ - One request handler for all requests.
32+
* `Late Span finish <test_late_span_finish>`_ - Late parent ``Span`` finish.
33+
* `Multiple callbacks <test_multiple_callbacks>`_ - Multiple callbacks spawned at the same time.
34+
* `Nested callbacks <test_nested_callbacks>`_ - One callback at a time, defined in a pipeline fashion.
35+
* `Subtask Span propagation <test_subtask_span_propagation>`_ - ``Span`` propagation for subtasks/coroutines.
36+
37+
Adding new patterns
38+
-------------------
39+
40+
A new pattern is composed of a directory under *testbed* with the *test_* prefix, and containing the files for each platform, also with the *test_* prefix:
41+
42+
.. code-block::
43+
44+
testbed/
45+
test_new_pattern/
46+
test_threads.py
47+
test_asyncio.py

ext/opentelemetry-ext-opentracing-shim/tests/testbed/__init__.py

Whitespace-only changes.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import opentelemetry.ext.opentracing_shim as opentracingshim
2+
from opentelemetry.sdk import trace
3+
from opentelemetry.sdk.trace.export import SimpleExportSpanProcessor
4+
from opentelemetry.sdk.trace.export.in_memory_span_exporter import (
5+
InMemorySpanExporter,
6+
)
7+
8+
9+
class MockTracer(opentracingshim.TracerShim):
10+
"""Wrapper of `opentracingshim.TracerShim`.
11+
12+
MockTracer extends `opentracingshim.TracerShim` by adding a in memory
13+
span exporter that can be used to get the list of finished spans."""
14+
15+
def __init__(self):
16+
tracer_provider = trace.TracerProvider()
17+
oteltracer = tracer_provider.get_tracer(__name__)
18+
super(MockTracer, self).__init__(oteltracer)
19+
exporter = InMemorySpanExporter()
20+
span_processor = SimpleExportSpanProcessor(exporter)
21+
tracer_provider.add_span_processor(span_processor)
22+
23+
self.exporter = exporter
24+
25+
def finished_spans(self):
26+
return self.exporter.get_finished_spans()
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
2+
Active Span replacement example.
3+
================================
4+
5+
This example shows a ``Span`` being created and then passed to an asynchronous task, which will temporary activate it to finish its processing, and further restore the previously active ``Span``.
6+
7+
``threading`` implementation:
8+
9+
.. code-block:: python
10+
11+
# Create a new Span for this task
12+
with self.tracer.start_active_span("task"):
13+
14+
with self.tracer.scope_manager.activate(span, True):
15+
# Simulate work strictly related to the initial Span
16+
pass
17+
18+
# Use the task span as parent of a new subtask
19+
with self.tracer.start_active_span("subtask"):
20+
pass

ext/opentelemetry-ext-opentracing-shim/tests/testbed/test_active_span_replacement/__init__.py

Whitespace-only changes.
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
from __future__ import print_function
2+
3+
import asyncio
4+
5+
from ..otel_ot_shim_tracer import MockTracer
6+
from ..testcase import OpenTelemetryTestCase
7+
from ..utils import stop_loop_when
8+
9+
10+
class TestAsyncio(OpenTelemetryTestCase):
11+
def setUp(self):
12+
self.tracer = MockTracer()
13+
self.loop = asyncio.get_event_loop()
14+
15+
def test_main(self):
16+
# Start an isolated task and query for its result -and finish it-
17+
# in another task/thread
18+
span = self.tracer.start_span("initial")
19+
self.submit_another_task(span)
20+
21+
stop_loop_when(
22+
self.loop,
23+
lambda: len(self.tracer.finished_spans()) >= 3,
24+
timeout=5.0,
25+
)
26+
self.loop.run_forever()
27+
28+
spans = self.tracer.finished_spans()
29+
self.assertEqual(len(spans), 3)
30+
self.assertNamesEqual(spans, ["initial", "subtask", "task"])
31+
32+
# task/subtask are part of the same trace,
33+
# and subtask is a child of task
34+
self.assertSameTrace(spans[1], spans[2])
35+
self.assertIsChildOf(spans[1], spans[2])
36+
37+
# initial task is not related in any way to those two tasks
38+
self.assertNotSameTrace(spans[0], spans[1])
39+
self.assertEqual(spans[0].parent, None)
40+
41+
async def task(self, span):
42+
# Create a new Span for this task
43+
with self.tracer.start_active_span("task"):
44+
45+
with self.tracer.scope_manager.activate(span, True):
46+
# Simulate work strictly related to the initial Span
47+
pass
48+
49+
# Use the task span as parent of a new subtask
50+
with self.tracer.start_active_span("subtask"):
51+
pass
52+
53+
def submit_another_task(self, span):
54+
self.loop.create_task(self.task(span))
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
from __future__ import print_function
2+
3+
from concurrent.futures import ThreadPoolExecutor
4+
5+
from ..otel_ot_shim_tracer import MockTracer
6+
from ..testcase import OpenTelemetryTestCase
7+
8+
9+
class TestThreads(OpenTelemetryTestCase):
10+
def setUp(self):
11+
self.tracer = MockTracer()
12+
# use max_workers=3 as a general example even if only one would suffice
13+
self.executor = ThreadPoolExecutor(max_workers=3)
14+
15+
def test_main(self):
16+
# Start an isolated task and query for its result -and finish it-
17+
# in another task/thread
18+
span = self.tracer.start_span("initial")
19+
self.submit_another_task(span)
20+
21+
self.executor.shutdown(True)
22+
23+
spans = self.tracer.finished_spans()
24+
self.assertEqual(len(spans), 3)
25+
self.assertNamesEqual(spans, ["initial", "subtask", "task"])
26+
27+
# task/subtask are part of the same trace,
28+
# and subtask is a child of task
29+
self.assertSameTrace(spans[1], spans[2])
30+
self.assertIsChildOf(spans[1], spans[2])
31+
32+
# initial task is not related in any way to those two tasks
33+
self.assertNotSameTrace(spans[0], spans[1])
34+
self.assertEqual(spans[0].parent, None)
35+
self.assertEqual(spans[2].parent, None)
36+
37+
def task(self, span):
38+
# Create a new Span for this task
39+
with self.tracer.start_active_span("task"):
40+
41+
with self.tracer.scope_manager.activate(span, True):
42+
# Simulate work strictly related to the initial Span
43+
pass
44+
45+
# Use the task span as parent of a new subtask
46+
with self.tracer.start_active_span("subtask"):
47+
pass
48+
49+
def submit_another_task(self, span):
50+
self.executor.submit(self.task, span)
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
2+
Client-Server example.
3+
======================
4+
5+
This example shows a ``Span`` created by a ``Client``, which will send a ``Message`` / ``SpanContext`` to a ``Server``, which will in turn extract such context and use it as parent of a new (server-side) ``Span``.
6+
7+
``Client.send()`` is used to send messages and inject the ``SpanContext`` using the ``TEXT_MAP`` format, and ``Server.process()`` will process received messages and will extract the context used as parent.
8+
9+
.. code-block:: python
10+
11+
def send(self):
12+
with self.tracer.start_active_span("send") as scope:
13+
scope.span.set_tag(tags.SPAN_KIND, tags.SPAN_KIND_RPC_CLIENT)
14+
15+
message = {}
16+
self.tracer.inject(scope.span.context,
17+
opentracing.Format.TEXT_MAP,
18+
message)
19+
self.queue.put(message)

ext/opentelemetry-ext-opentracing-shim/tests/testbed/test_client_server/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)