opentelemetry-python-contrib/reference/tests/contrib/bottle/test.py

414 lines
13 KiB
Python

import bottle
import ddtrace
import webtest
from tests.opentracer.utils import init_tracer
from ...base import BaseTracerTestCase
from ...utils import assert_span_http_status_code
from ddtrace import compat
from ddtrace.constants import ANALYTICS_SAMPLE_RATE_KEY
from ddtrace.contrib.bottle import TracePlugin
from ddtrace.ext import http
SERVICE = 'bottle-app'
class TraceBottleTest(BaseTracerTestCase):
"""
Ensures that Bottle is properly traced.
"""
def setUp(self):
super(TraceBottleTest, self).setUp()
# provide a dummy tracer
self._original_tracer = ddtrace.tracer
ddtrace.tracer = self.tracer
# provide a Bottle app
self.app = bottle.Bottle()
def tearDown(self):
# restore the tracer
ddtrace.tracer = self._original_tracer
def _trace_app(self, tracer=None):
self.app.install(TracePlugin(service=SERVICE, tracer=tracer))
self.app = webtest.TestApp(self.app)
def test_200(self, query_string=''):
if query_string:
fqs = '?' + query_string
else:
fqs = ''
# setup our test app
@self.app.route('/hi/<name>')
def hi(name):
return 'hi %s' % name
self._trace_app(self.tracer)
# make a request
resp = self.app.get('/hi/dougie' + fqs)
assert resp.status_int == 200
assert compat.to_unicode(resp.body) == u'hi dougie'
# validate it's traced
spans = self.tracer.writer.pop()
assert len(spans) == 1
s = spans[0]
assert s.name == 'bottle.request'
assert s.service == 'bottle-app'
assert s.span_type == 'web'
assert s.resource == 'GET /hi/<name>'
assert_span_http_status_code(s, 200)
assert s.get_tag('http.method') == 'GET'
assert s.get_tag(http.URL) == 'http://localhost:80/hi/dougie'
if ddtrace.config.bottle.trace_query_string:
assert s.get_tag(http.QUERY_STRING) == query_string
else:
assert http.QUERY_STRING not in s.meta
services = self.tracer.writer.pop_services()
assert services == {}
def test_query_string(self):
return self.test_200('foo=bar')
def test_query_string_multi_keys(self):
return self.test_200('foo=bar&foo=baz&x=y')
def test_query_string_trace(self):
with self.override_http_config('bottle', dict(trace_query_string=True)):
return self.test_200('foo=bar')
def test_query_string_multi_keys_trace(self):
with self.override_http_config('bottle', dict(trace_query_string=True)):
return self.test_200('foo=bar&foo=baz&x=y')
def test_2xx(self):
@self.app.route('/2xx')
def handled():
return bottle.HTTPResponse("", status=202)
self._trace_app(self.tracer)
# make a request
try:
self.app.get('/2xx')
except webtest.AppError:
pass
spans = self.tracer.writer.pop()
assert len(spans) == 1
s = spans[0]
assert s.resource == 'GET /2xx'
assert_span_http_status_code(s, 202)
assert s.error == 0
def test_400_return(self):
@self.app.route('/400_return')
def handled400():
return bottle.HTTPResponse(status=400)
self._trace_app(self.tracer)
# make a request
try:
self.app.get('/400_return')
except webtest.AppError:
pass
spans = self.tracer.writer.pop()
assert len(spans) == 1
s = spans[0]
assert s.name == 'bottle.request'
assert s.service == 'bottle-app'
assert s.resource == 'GET /400_return'
assert_span_http_status_code(s, 400)
assert s.get_tag('http.method') == 'GET'
assert s.get_tag(http.URL) == 'http://localhost:80/400_return'
assert s.error == 0
def test_400_raise(self):
@self.app.route('/400_raise')
def handled400():
raise bottle.HTTPResponse(status=400)
self._trace_app(self.tracer)
# make a request
try:
self.app.get('/400_raise')
except webtest.AppError:
pass
spans = self.tracer.writer.pop()
assert len(spans) == 1
s = spans[0]
assert s.name == 'bottle.request'
assert s.service == 'bottle-app'
assert s.resource == 'GET /400_raise'
assert_span_http_status_code(s, 400)
assert s.get_tag('http.method') == 'GET'
assert s.get_tag(http.URL) == 'http://localhost:80/400_raise'
assert s.error == 1
def test_500(self):
@self.app.route('/hi')
def hi():
raise Exception('oh no')
self._trace_app(self.tracer)
# make a request
try:
self.app.get('/hi')
except webtest.AppError:
pass
spans = self.tracer.writer.pop()
assert len(spans) == 1
s = spans[0]
assert s.name == 'bottle.request'
assert s.service == 'bottle-app'
assert s.resource == 'GET /hi'
assert_span_http_status_code(s, 500)
assert s.get_tag('http.method') == 'GET'
assert s.get_tag(http.URL) == 'http://localhost:80/hi'
assert s.error == 1
def test_5XX_response(self):
"""
When a 5XX response is returned
The span error attribute should be 1
"""
@self.app.route('/5XX-1')
def handled500_1():
raise bottle.HTTPResponse(status=503)
@self.app.route('/5XX-2')
def handled500_2():
raise bottle.HTTPError(status=502)
@self.app.route('/5XX-3')
def handled500_3():
bottle.response.status = 503
return 'hmmm'
self._trace_app(self.tracer)
try:
self.app.get('/5XX-1')
except webtest.AppError:
pass
spans = self.tracer.writer.pop()
assert len(spans) == 1
assert spans[0].error == 1
try:
self.app.get('/5XX-2')
except webtest.AppError:
pass
spans = self.tracer.writer.pop()
assert len(spans) == 1
assert spans[0].error == 1
try:
self.app.get('/5XX-3')
except webtest.AppError:
pass
spans = self.tracer.writer.pop()
assert len(spans) == 1
assert spans[0].error == 1
def test_abort(self):
@self.app.route('/hi')
def hi():
raise bottle.abort(420, 'Enhance Your Calm')
self._trace_app(self.tracer)
# make a request
try:
self.app.get('/hi')
except webtest.AppError:
pass
spans = self.tracer.writer.pop()
assert len(spans) == 1
s = spans[0]
assert s.name == 'bottle.request'
assert s.service == 'bottle-app'
assert s.resource == 'GET /hi'
assert_span_http_status_code(s, 420)
assert s.get_tag('http.method') == 'GET'
assert s.get_tag(http.URL) == 'http://localhost:80/hi'
def test_bottle_global_tracer(self):
# without providing a Tracer instance, it should work
@self.app.route('/home/')
def home():
return 'Hello world'
self._trace_app()
# make a request
resp = self.app.get('/home/')
assert resp.status_int == 200
# validate it's traced
spans = self.tracer.writer.pop()
assert len(spans) == 1
s = spans[0]
assert s.name == 'bottle.request'
assert s.service == 'bottle-app'
assert s.resource == 'GET /home/'
assert_span_http_status_code(s, 200)
assert s.get_tag('http.method') == 'GET'
assert s.get_tag(http.URL) == 'http://localhost:80/home/'
def test_analytics_global_on_integration_default(self):
"""
When making a request
When an integration trace search is not event sample rate is not set and globally trace search is enabled
We expect the root span to have the appropriate tag
"""
# setup our test app
@self.app.route('/hi/<name>')
def hi(name):
return 'hi %s' % name
self._trace_app(self.tracer)
with self.override_global_config(dict(analytics_enabled=True)):
resp = self.app.get('/hi/dougie')
assert resp.status_int == 200
assert compat.to_unicode(resp.body) == u'hi dougie'
root = self.get_root_span()
root.assert_matches(
name='bottle.request',
metrics={
ANALYTICS_SAMPLE_RATE_KEY: 1.0,
},
)
for span in self.spans:
if span == root:
continue
self.assertIsNone(span.get_metric(ANALYTICS_SAMPLE_RATE_KEY))
def test_analytics_global_on_integration_on(self):
"""
When making a request
When an integration trace search is enabled and sample rate is set and globally trace search is enabled
We expect the root span to have the appropriate tag
"""
# setup our test app
@self.app.route('/hi/<name>')
def hi(name):
return 'hi %s' % name
self._trace_app(self.tracer)
with self.override_global_config(dict(analytics_enabled=True)):
with self.override_config('bottle', dict(analytics_enabled=True, analytics_sample_rate=0.5)):
resp = self.app.get('/hi/dougie')
assert resp.status_int == 200
assert compat.to_unicode(resp.body) == u'hi dougie'
root = self.get_root_span()
root.assert_matches(
name='bottle.request',
metrics={
ANALYTICS_SAMPLE_RATE_KEY: 0.5,
},
)
for span in self.spans:
if span == root:
continue
self.assertIsNone(span.get_metric(ANALYTICS_SAMPLE_RATE_KEY))
def test_analytics_global_off_integration_default(self):
"""
When making a request
When an integration trace search is not set and sample rate is set and globally trace search is disabled
We expect the root span to not include tag
"""
# setup our test app
@self.app.route('/hi/<name>')
def hi(name):
return 'hi %s' % name
self._trace_app(self.tracer)
with self.override_global_config(dict(analytics_enabled=False)):
resp = self.app.get('/hi/dougie')
assert resp.status_int == 200
assert compat.to_unicode(resp.body) == u'hi dougie'
root = self.get_root_span()
self.assertIsNone(root.get_metric(ANALYTICS_SAMPLE_RATE_KEY))
for span in self.spans:
if span == root:
continue
self.assertIsNone(span.get_metric(ANALYTICS_SAMPLE_RATE_KEY))
def test_analytics_global_off_integration_on(self):
"""
When making a request
When an integration trace search is enabled and sample rate is set and globally trace search is disabled
We expect the root span to have the appropriate tag
"""
# setup our test app
@self.app.route('/hi/<name>')
def hi(name):
return 'hi %s' % name
self._trace_app(self.tracer)
with self.override_global_config(dict(analytics_enabled=False)):
with self.override_config('bottle', dict(analytics_enabled=True, analytics_sample_rate=0.5)):
resp = self.app.get('/hi/dougie')
assert resp.status_int == 200
assert compat.to_unicode(resp.body) == u'hi dougie'
root = self.get_root_span()
root.assert_matches(
name='bottle.request',
metrics={
ANALYTICS_SAMPLE_RATE_KEY: 0.5,
},
)
for span in self.spans:
if span == root:
continue
self.assertIsNone(span.get_metric(ANALYTICS_SAMPLE_RATE_KEY))
def test_200_ot(self):
ot_tracer = init_tracer('my_svc', self.tracer)
# setup our test app
@self.app.route('/hi/<name>')
def hi(name):
return 'hi %s' % name
self._trace_app(self.tracer)
# make a request
with ot_tracer.start_active_span('ot_span'):
resp = self.app.get('/hi/dougie')
assert resp.status_int == 200
assert compat.to_unicode(resp.body) == u'hi dougie'
# validate it's traced
spans = self.tracer.writer.pop()
assert len(spans) == 2
ot_span, dd_span = spans
# confirm the parenting
assert ot_span.parent_id is None
assert dd_span.parent_id == ot_span.span_id
assert ot_span.resource == 'ot_span'
assert dd_span.name == 'bottle.request'
assert dd_span.service == 'bottle-app'
assert dd_span.resource == 'GET /hi/<name>'
assert_span_http_status_code(dd_span, 200)
assert dd_span.get_tag('http.method') == 'GET'
assert dd_span.get_tag(http.URL) == 'http://localhost:80/hi/dougie'
services = self.tracer.writer.pop_services()
assert services == {}