Thanks to visit codestin.com
Credit goes to github.com

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 <[email protected]> Co-authored-by: alrex <[email protected]>
1 parent 3ad6ac5 commit acb7f48

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.
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
from __future__ import print_function
2+
3+
import asyncio
4+
5+
import opentracing
6+
from opentracing.ext import tags
7+
8+
from ..otel_ot_shim_tracer import MockTracer
9+
from ..testcase import OpenTelemetryTestCase
10+
from ..utils import get_logger, get_one_by_tag, stop_loop_when
11+
12+
logger = get_logger(__name__)
13+
14+
15+
class Server:
16+
def __init__(self, *args, **kwargs):
17+
tracer = kwargs.pop("tracer")
18+
queue = kwargs.pop("queue")
19+
super(Server, self).__init__(*args, **kwargs)
20+
21+
self.tracer = tracer
22+
self.queue = queue
23+
24+
async def run(self):
25+
value = await self.queue.get()
26+
self.process(value)
27+
28+
def process(self, message):
29+
logger.info("Processing message in server")
30+
31+
ctx = self.tracer.extract(opentracing.Format.TEXT_MAP, message)
32+
with self.tracer.start_active_span("receive", child_of=ctx) as scope:
33+
scope.span.set_tag(tags.SPAN_KIND, tags.SPAN_KIND_RPC_SERVER)
34+
35+
36+
class Client:
37+
def __init__(self, tracer, queue):
38+
self.tracer = tracer
39+
self.queue = queue
40+
41+
async def send(self):
42+
with self.tracer.start_active_span("send") as scope:
43+
scope.span.set_tag(tags.SPAN_KIND, tags.SPAN_KIND_RPC_CLIENT)
44+
45+
message = {}
46+
self.tracer.inject(
47+
scope.span.context, opentracing.Format.TEXT_MAP, message
48+
)
49+
await self.queue.put(message)
50+
51+
logger.info("Sent message from client")
52+
53+
54+
class TestAsyncio(OpenTelemetryTestCase):
55+
def setUp(self):
56+
self.tracer = MockTracer()
57+
self.queue = asyncio.Queue()
58+
self.loop = asyncio.get_event_loop()
59+
self.server = Server(tracer=self.tracer, queue=self.queue)
60+
61+
def test(self):
62+
client = Client(self.tracer, self.queue)
63+
self.loop.create_task(self.server.run())
64+
self.loop.create_task(client.send())
65+
66+
stop_loop_when(
67+
self.loop,
68+
lambda: len(self.tracer.finished_spans()) >= 2,
69+
timeout=5.0,
70+
)
71+
self.loop.run_forever()
72+
73+
spans = self.tracer.finished_spans()
74+
self.assertIsNotNone(
75+
get_one_by_tag(spans, tags.SPAN_KIND, tags.SPAN_KIND_RPC_SERVER)
76+
)
77+
self.assertIsNotNone(
78+
get_one_by_tag(spans, tags.SPAN_KIND, tags.SPAN_KIND_RPC_CLIENT)
79+
)
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
from __future__ import print_function
2+
3+
from queue import Queue
4+
from threading import Thread
5+
6+
import opentracing
7+
from opentracing.ext import tags
8+
9+
from ..otel_ot_shim_tracer import MockTracer
10+
from ..testcase import OpenTelemetryTestCase
11+
from ..utils import await_until, get_logger, get_one_by_tag
12+
13+
logger = get_logger(__name__)
14+
15+
16+
class Server(Thread):
17+
def __init__(self, *args, **kwargs):
18+
tracer = kwargs.pop("tracer")
19+
queue = kwargs.pop("queue")
20+
super(Server, self).__init__(*args, **kwargs)
21+
22+
self.daemon = True
23+
self.tracer = tracer
24+
self.queue = queue
25+
26+
def run(self):
27+
value = self.queue.get()
28+
self.process(value)
29+
30+
def process(self, message):
31+
logger.info("Processing message in server")
32+
33+
ctx = self.tracer.extract(opentracing.Format.TEXT_MAP, message)
34+
with self.tracer.start_active_span("receive", child_of=ctx) as scope:
35+
scope.span.set_tag(tags.SPAN_KIND, tags.SPAN_KIND_RPC_SERVER)
36+
37+
38+
class Client:
39+
def __init__(self, tracer, queue):
40+
self.tracer = tracer
41+
self.queue = queue
42+
43+
def send(self):
44+
with self.tracer.start_active_span("send") as scope:
45+
scope.span.set_tag(tags.SPAN_KIND, tags.SPAN_KIND_RPC_CLIENT)
46+
47+
message = {}
48+
self.tracer.inject(
49+
scope.span.context, opentracing.Format.TEXT_MAP, message
50+
)
51+
self.queue.put(message)
52+
53+
logger.info("Sent message from client")
54+
55+
56+
class TestThreads(OpenTelemetryTestCase):
57+
def setUp(self):
58+
self.tracer = MockTracer()
59+
self.queue = Queue()
60+
self.server = Server(tracer=self.tracer, queue=self.queue)
61+
self.server.start()
62+
63+
def test(self):
64+
client = Client(self.tracer, self.queue)
65+
client.send()
66+
67+
await_until(lambda: len(self.tracer.finished_spans()) >= 2)
68+
69+
spans = self.tracer.finished_spans()
70+
self.assertIsNotNone(
71+
get_one_by_tag(spans, tags.SPAN_KIND, tags.SPAN_KIND_RPC_SERVER)
72+
)
73+
self.assertIsNotNone(
74+
get_one_by_tag(spans, tags.SPAN_KIND, tags.SPAN_KIND_RPC_CLIENT)
75+
)

0 commit comments

Comments
 (0)