mirror of
https://github.com/open-telemetry/opentelemetry-python-contrib.git
synced 2025-08-02 02:52:18 +08:00
159 lines
4.7 KiB
Python
159 lines
4.7 KiB
Python
import threading
|
|
|
|
from tornado import httpclient
|
|
from tornado.testing import gen_test
|
|
|
|
from ddtrace.contrib.tornado import patch, unpatch
|
|
from ddtrace.ext import http
|
|
|
|
from . import web
|
|
from .web.app import CustomDefaultHandler
|
|
from .utils import TornadoTestCase
|
|
|
|
|
|
class TestAsyncConcurrency(TornadoTestCase):
|
|
"""
|
|
Ensure that application instrumentation doesn't break asynchronous concurrency.
|
|
"""
|
|
@gen_test
|
|
def test_concurrent_requests(self):
|
|
REQUESTS_NUMBER = 25
|
|
responses = []
|
|
|
|
# the application must handle concurrent calls
|
|
def make_requests():
|
|
# use a blocking HTTP client (we're in another thread)
|
|
http_client = httpclient.HTTPClient()
|
|
url = self.get_url('/nested/')
|
|
response = http_client.fetch(url)
|
|
responses.append(response)
|
|
assert 200 == response.code
|
|
assert 'OK' == response.body.decode('utf-8')
|
|
# freeing file descriptors
|
|
http_client.close()
|
|
|
|
# blocking call executed in different threads
|
|
threads = [threading.Thread(target=make_requests) for _ in range(REQUESTS_NUMBER)]
|
|
for t in threads:
|
|
t.start()
|
|
|
|
while len(responses) < REQUESTS_NUMBER:
|
|
yield web.compat.sleep(0.001)
|
|
|
|
for t in threads:
|
|
t.join()
|
|
|
|
# the trace is created
|
|
traces = self.tracer.writer.pop_traces()
|
|
assert REQUESTS_NUMBER == len(traces)
|
|
assert 2 == len(traces[0])
|
|
|
|
|
|
class TestAppSafety(TornadoTestCase):
|
|
"""
|
|
Ensure that the application patch has the proper safety guards.
|
|
"""
|
|
|
|
def test_trace_unpatch(self):
|
|
# the application must not be traced if unpatch() is called
|
|
patch()
|
|
unpatch()
|
|
|
|
response = self.fetch('/success/')
|
|
assert 200 == response.code
|
|
|
|
traces = self.tracer.writer.pop_traces()
|
|
assert 0 == len(traces)
|
|
|
|
def test_trace_unpatch_not_traced(self):
|
|
# the untrace must be safe if the app is not traced
|
|
unpatch()
|
|
unpatch()
|
|
|
|
response = self.fetch('/success/')
|
|
assert 200 == response.code
|
|
|
|
traces = self.tracer.writer.pop_traces()
|
|
assert 0 == len(traces)
|
|
|
|
def test_trace_app_twice(self):
|
|
# the application must not be traced multiple times
|
|
patch()
|
|
patch()
|
|
|
|
response = self.fetch('/success/')
|
|
assert 200 == response.code
|
|
|
|
traces = self.tracer.writer.pop_traces()
|
|
assert 1 == len(traces)
|
|
assert 1 == len(traces[0])
|
|
|
|
def test_arbitrary_resource_querystring(self):
|
|
# users inputs should not determine `span.resource` field
|
|
response = self.fetch('/success/?magic_number=42')
|
|
assert 200 == response.code
|
|
|
|
traces = self.tracer.writer.pop_traces()
|
|
assert 1 == len(traces)
|
|
assert 1 == len(traces[0])
|
|
|
|
request_span = traces[0][0]
|
|
assert 'tests.contrib.tornado.web.app.SuccessHandler' == request_span.resource
|
|
assert self.get_url('/success/') == request_span.get_tag(http.URL)
|
|
|
|
def test_arbitrary_resource_404(self):
|
|
# users inputs should not determine `span.resource` field
|
|
response = self.fetch('/does_not_exist/')
|
|
assert 404 == response.code
|
|
|
|
traces = self.tracer.writer.pop_traces()
|
|
assert 1 == len(traces)
|
|
assert 1 == len(traces[0])
|
|
|
|
request_span = traces[0][0]
|
|
assert 'tornado.web.ErrorHandler' == request_span.resource
|
|
assert self.get_url('/does_not_exist/') == request_span.get_tag(http.URL)
|
|
|
|
@gen_test
|
|
def test_futures_without_context(self):
|
|
# ensures that if futures propagation is available, an empty
|
|
# context doesn't crash the system
|
|
from .web.compat import ThreadPoolExecutor
|
|
|
|
def job():
|
|
with self.tracer.trace('job'):
|
|
return 42
|
|
|
|
executor = ThreadPoolExecutor(max_workers=3)
|
|
yield executor.submit(job)
|
|
|
|
traces = self.tracer.writer.pop_traces()
|
|
assert 1 == len(traces)
|
|
assert 1 == len(traces[0])
|
|
|
|
# this trace yields the execution of the thread
|
|
span = traces[0][0]
|
|
assert 'job' == span.name
|
|
|
|
|
|
class TestCustomAppSafety(TornadoTestCase):
|
|
"""
|
|
Ensure that the application patch has the proper safety guards,
|
|
even for custom default handlers.
|
|
"""
|
|
def get_settings(self):
|
|
return {
|
|
'default_handler_class': CustomDefaultHandler,
|
|
'default_handler_args': dict(status_code=400),
|
|
}
|
|
|
|
def test_trace_unpatch(self):
|
|
# the application must not be traced if unpatch() is called
|
|
unpatch()
|
|
|
|
response = self.fetch('/custom_handler/')
|
|
assert 400 == response.code
|
|
|
|
traces = self.tracer.writer.pop_traces()
|
|
assert 0 == len(traces)
|