opentelemetry-python-contrib/instrumentation/opentelemetry-instrumentati.../tests/test_instrumentation.py

820 lines
29 KiB
Python

# Copyright The 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.
from unittest.mock import Mock, patch
from http_server_mock import HttpServerMock
from tornado.httpclient import HTTPClientError
from tornado.testing import AsyncHTTPTestCase
from opentelemetry import trace
from opentelemetry.instrumentation.propagators import (
TraceResponsePropagator,
get_global_response_propagator,
set_global_response_propagator,
)
from opentelemetry.instrumentation.tornado import (
TornadoInstrumentor,
patch_handler_class,
unpatch_handler_class,
)
from opentelemetry.semconv._incubating.attributes.http_attributes import (
HTTP_CLIENT_IP,
HTTP_HOST,
HTTP_METHOD,
HTTP_SCHEME,
HTTP_STATUS_CODE,
HTTP_TARGET,
HTTP_URL,
)
from opentelemetry.semconv._incubating.attributes.net_attributes import (
NET_PEER_IP,
)
from opentelemetry.test.test_base import TestBase
from opentelemetry.test.wsgitestutil import WsgiTestBase
from opentelemetry.trace import SpanKind, StatusCode
from opentelemetry.util.http import (
OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST,
OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE,
get_excluded_urls,
get_traced_request_attrs,
)
from .tornado_test_app import (
AsyncHandler,
DynamicHandler,
MainHandler,
make_app,
)
class TornadoTest(AsyncHTTPTestCase, TestBase):
# pylint:disable=no-self-use
def get_app(self):
tracer = trace.get_tracer(__name__)
app = make_app(tracer)
return app
def setUp(self):
super().setUp()
TornadoInstrumentor().instrument(
server_request_hook=getattr(self, "server_request_hook", None),
client_request_hook=getattr(self, "client_request_hook", None),
client_response_hook=getattr(self, "client_response_hook", None),
)
# pylint: disable=protected-access
self.env_patch = patch.dict(
"os.environ",
{
"OTEL_PYTHON_TORNADO_EXCLUDED_URLS": "healthz,ping",
"OTEL_PYTHON_TORNADO_TRACED_REQUEST_ATTRS": "uri,full_url,query",
},
)
self.env_patch.start()
self.exclude_patch = patch(
"opentelemetry.instrumentation.tornado._excluded_urls",
get_excluded_urls("TORNADO"),
)
self.traced_patch = patch(
"opentelemetry.instrumentation.tornado._traced_request_attrs",
get_traced_request_attrs("TORNADO"),
)
self.exclude_patch.start()
self.traced_patch.start()
def tearDown(self):
TornadoInstrumentor().uninstrument()
self.env_patch.stop()
self.exclude_patch.stop()
self.traced_patch.stop()
super().tearDown()
class TestTornadoInstrumentor(TornadoTest):
def test_patch_references(self):
self.assertEqual(len(TornadoInstrumentor().patched_handlers), 0)
self.fetch("/")
self.fetch("/async")
self.assertEqual(
TornadoInstrumentor().patched_handlers, [MainHandler, AsyncHandler]
)
self.fetch("/async")
self.fetch("/")
self.assertEqual(
TornadoInstrumentor().patched_handlers, [MainHandler, AsyncHandler]
)
TornadoInstrumentor().uninstrument()
self.assertEqual(TornadoInstrumentor().patched_handlers, [])
def test_patch_applied_only_once(self):
tracer = trace.get_tracer(__name__)
self.assertTrue(patch_handler_class(tracer, {}, AsyncHandler))
self.assertFalse(patch_handler_class(tracer, {}, AsyncHandler))
self.assertFalse(patch_handler_class(tracer, {}, AsyncHandler))
unpatch_handler_class(AsyncHandler)
class TestTornadoInstrumentation(TornadoTest, WsgiTestBase):
def test_http_calls(self):
methods = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]
for method in methods:
self._test_http_method_call(method)
def _test_http_method_call(self, method):
body = "" if method in ["POST", "PUT", "PATCH"] else None
response = self.fetch("/", method=method, body=body)
self.assertEqual(response.code, 201)
spans = self.memory_exporter.get_finished_spans()
self.assertEqual(len(spans), 3)
manual, server, client = self.sorted_spans(spans)
self.assertEqual(manual.name, "manual")
self.assertEqual(manual.parent, server.context)
self.assertEqual(manual.context.trace_id, client.context.trace_id)
self.assertEqual(server.name, f"{method} /")
self.assertTrue(server.parent.is_remote)
self.assertNotEqual(server.parent, client.context)
self.assertEqual(server.parent.span_id, client.context.span_id)
self.assertEqual(server.context.trace_id, client.context.trace_id)
self.assertEqual(server.kind, SpanKind.SERVER)
self.assertSpanHasAttributes(
server,
{
HTTP_METHOD: method,
HTTP_SCHEME: "http",
HTTP_HOST: "127.0.0.1:" + str(self.get_http_port()),
HTTP_TARGET: "/",
HTTP_CLIENT_IP: "127.0.0.1",
HTTP_STATUS_CODE: 201,
"tornado.handler": "tests.tornado_test_app.MainHandler",
},
)
self.assertEqual(client.name, method)
self.assertFalse(client.context.is_remote)
self.assertIsNone(client.parent)
self.assertEqual(client.kind, SpanKind.CLIENT)
self.assertSpanHasAttributes(
client,
{
HTTP_URL: self.get_url("/"),
HTTP_METHOD: method,
HTTP_STATUS_CODE: 201,
},
)
self.memory_exporter.clear()
def test_not_recording(self):
mock_tracer = Mock()
mock_span = Mock()
mock_span.is_recording.return_value = False
mock_tracer.start_span.return_value = mock_span
with patch("opentelemetry.trace.get_tracer") as tracer:
tracer.return_value = mock_tracer
self.fetch("/")
self.assertFalse(mock_span.is_recording())
self.assertTrue(mock_span.is_recording.called)
self.assertFalse(mock_span.set_attribute.called)
self.assertFalse(mock_span.set_status.called)
def test_async_handler(self):
self._test_async_handler("/async", "AsyncHandler")
def test_coroutine_handler(self):
self._test_async_handler("/cor", "CoroutineHandler")
def _test_async_handler(self, url, handler_name):
response = self.fetch(url)
self.assertEqual(response.code, 201)
spans = self.get_finished_spans()
self.assertEqual(len(spans), 5)
client = spans.by_name("GET")
server = spans.by_name(f"GET {url}")
sub_wrapper = spans.by_name("sub-task-wrapper")
sub2 = spans.by_name("sub-task-2")
self.assertEqual(sub2.name, "sub-task-2")
self.assertEqual(sub2.parent, sub_wrapper.context)
self.assertEqual(sub2.context.trace_id, client.context.trace_id)
sub1 = spans.by_name("sub-task-1")
self.assertEqual(sub1.name, "sub-task-1")
self.assertEqual(sub1.parent, sub_wrapper.context)
self.assertEqual(sub1.context.trace_id, client.context.trace_id)
self.assertEqual(sub_wrapper.name, "sub-task-wrapper")
self.assertEqual(sub_wrapper.parent, server.context)
self.assertEqual(sub_wrapper.context.trace_id, client.context.trace_id)
self.assertEqual(server.name, f"GET {url}")
self.assertTrue(server.parent.is_remote)
self.assertNotEqual(server.parent, client.context)
self.assertEqual(server.parent.span_id, client.context.span_id)
self.assertEqual(server.context.trace_id, client.context.trace_id)
self.assertEqual(server.kind, SpanKind.SERVER)
self.assertSpanHasAttributes(
server,
{
HTTP_METHOD: "GET",
HTTP_SCHEME: "http",
HTTP_HOST: "127.0.0.1:" + str(self.get_http_port()),
HTTP_TARGET: url,
HTTP_CLIENT_IP: "127.0.0.1",
HTTP_STATUS_CODE: 201,
"tornado.handler": f"tests.tornado_test_app.{handler_name}",
},
)
self.assertEqual(client.name, "GET")
self.assertFalse(client.context.is_remote)
self.assertIsNone(client.parent)
self.assertEqual(client.kind, SpanKind.CLIENT)
self.assertSpanHasAttributes(
client,
{
HTTP_URL: self.get_url(url),
HTTP_METHOD: "GET",
HTTP_STATUS_CODE: 201,
},
)
def test_500(self):
response = self.fetch("/error")
self.assertEqual(response.code, 500)
spans = self.get_finished_spans()
self.assertEqual(len(spans), 2)
client = spans.by_name("GET")
server = spans.by_name("GET /error")
self.assertEqual(server.name, "GET /error")
self.assertEqual(server.kind, SpanKind.SERVER)
self.assertSpanHasAttributes(
server,
{
HTTP_METHOD: "GET",
HTTP_SCHEME: "http",
HTTP_HOST: "127.0.0.1:" + str(self.get_http_port()),
HTTP_TARGET: "/error",
HTTP_CLIENT_IP: "127.0.0.1",
HTTP_STATUS_CODE: 500,
"tornado.handler": "tests.tornado_test_app.BadHandler",
},
)
self.assertEqual(client.name, "GET")
self.assertEqual(client.kind, SpanKind.CLIENT)
self.assertSpanHasAttributes(
client,
{
HTTP_URL: self.get_url("/error"),
HTTP_METHOD: "GET",
HTTP_STATUS_CODE: 500,
},
)
def test_404(self):
response = self.fetch("/missing-url")
self.assertEqual(response.code, 404)
spans = self.sorted_spans(self.memory_exporter.get_finished_spans())
self.assertEqual(len(spans), 2)
server, client = spans
self.assertEqual(server.name, "GET /missing-url")
self.assertEqual(server.kind, SpanKind.SERVER)
self.assertSpanHasAttributes(
server,
{
HTTP_METHOD: "GET",
HTTP_SCHEME: "http",
HTTP_HOST: "127.0.0.1:" + str(self.get_http_port()),
HTTP_TARGET: "/missing-url",
HTTP_CLIENT_IP: "127.0.0.1",
HTTP_STATUS_CODE: 404,
"tornado.handler": "tornado.web.ErrorHandler",
},
)
self.assertEqual(client.name, "GET")
self.assertEqual(client.kind, SpanKind.CLIENT)
self.assertSpanHasAttributes(
client,
{
HTTP_URL: self.get_url("/missing-url"),
HTTP_METHOD: "GET",
HTTP_STATUS_CODE: 404,
},
)
def test_http_error(self):
response = self.fetch("/raise_403")
self.assertEqual(response.code, 403)
spans = self.sorted_spans(self.memory_exporter.get_finished_spans())
self.assertEqual(len(spans), 2)
server, client = spans
self.assertEqual(server.name, "GET /raise_403")
self.assertEqual(server.kind, SpanKind.SERVER)
self.assertSpanHasAttributes(
server,
{
HTTP_METHOD: "GET",
HTTP_SCHEME: "http",
HTTP_HOST: "127.0.0.1:" + str(self.get_http_port()),
HTTP_TARGET: "/raise_403",
HTTP_CLIENT_IP: "127.0.0.1",
HTTP_STATUS_CODE: 403,
"tornado.handler": "tests.tornado_test_app.RaiseHTTPErrorHandler",
},
)
self.assertEqual(client.name, "GET")
self.assertEqual(client.kind, SpanKind.CLIENT)
self.assertSpanHasAttributes(
client,
{
HTTP_URL: self.get_url("/raise_403"),
HTTP_METHOD: "GET",
HTTP_STATUS_CODE: 403,
},
)
def test_dynamic_handler(self):
response = self.fetch("/dyna")
self.assertEqual(response.code, 404)
self.memory_exporter.clear()
self._app.add_handlers(r".+", [(r"/dyna", DynamicHandler)])
response = self.fetch("/dyna")
self.assertEqual(response.code, 202)
spans = self.sorted_spans(self.memory_exporter.get_finished_spans())
self.assertEqual(len(spans), 2)
server, client = spans
self.assertEqual(server.name, "GET /dyna")
self.assertTrue(server.parent.is_remote)
self.assertNotEqual(server.parent, client.context)
self.assertEqual(server.parent.span_id, client.context.span_id)
self.assertEqual(server.context.trace_id, client.context.trace_id)
self.assertEqual(server.kind, SpanKind.SERVER)
self.assertSpanHasAttributes(
server,
{
HTTP_METHOD: "GET",
HTTP_SCHEME: "http",
HTTP_HOST: "127.0.0.1:" + str(self.get_http_port()),
HTTP_TARGET: "/dyna",
HTTP_CLIENT_IP: "127.0.0.1",
HTTP_STATUS_CODE: 202,
"tornado.handler": "tests.tornado_test_app.DynamicHandler",
},
)
self.assertEqual(client.name, "GET")
self.assertFalse(client.context.is_remote)
self.assertIsNone(client.parent)
self.assertEqual(client.kind, SpanKind.CLIENT)
self.assertSpanHasAttributes(
client,
{
HTTP_URL: self.get_url("/dyna"),
HTTP_METHOD: "GET",
HTTP_STATUS_CODE: 202,
},
)
def test_handler_on_finish(self):
response = self.fetch("/on_finish")
self.assertEqual(response.code, 200)
spans = self.sorted_spans(self.memory_exporter.get_finished_spans())
self.assertEqual(len(spans), 3)
auditor, server, client = spans
self.assertEqual(server.name, "GET /on_finish")
self.assertTrue(server.parent.is_remote)
self.assertNotEqual(server.parent, client.context)
self.assertEqual(server.parent.span_id, client.context.span_id)
self.assertEqual(server.context.trace_id, client.context.trace_id)
self.assertEqual(server.kind, SpanKind.SERVER)
self.assertSpanHasAttributes(
server,
{
HTTP_METHOD: "GET",
HTTP_SCHEME: "http",
HTTP_HOST: "127.0.0.1:" + str(self.get_http_port()),
HTTP_TARGET: "/on_finish",
HTTP_CLIENT_IP: "127.0.0.1",
HTTP_STATUS_CODE: 200,
"tornado.handler": "tests.tornado_test_app.FinishedHandler",
},
)
self.assertEqual(client.name, "GET")
self.assertFalse(client.context.is_remote)
self.assertIsNone(client.parent)
self.assertEqual(client.kind, SpanKind.CLIENT)
self.assertSpanHasAttributes(
client,
{
HTTP_URL: self.get_url("/on_finish"),
HTTP_METHOD: "GET",
HTTP_STATUS_CODE: 200,
},
)
self.assertEqual(auditor.name, "audit_task")
self.assertFalse(auditor.context.is_remote)
self.assertEqual(auditor.parent.span_id, server.context.span_id)
self.assertEqual(auditor.context.trace_id, client.context.trace_id)
self.assertEqual(auditor.kind, SpanKind.INTERNAL)
def test_exclude_lists(self):
def test_excluded(path):
self.fetch(path)
spans = self.sorted_spans(
self.memory_exporter.get_finished_spans()
)
self.assertEqual(len(spans), 1)
client = spans[0]
self.assertEqual(client.name, "GET")
self.assertEqual(client.kind, SpanKind.CLIENT)
self.assertSpanHasAttributes(
client,
{
HTTP_URL: self.get_url(path),
HTTP_METHOD: "GET",
HTTP_STATUS_CODE: 200,
},
)
self.memory_exporter.clear()
test_excluded("/healthz")
test_excluded("/ping")
def test_traced_attrs(self):
self.fetch("/pong?q=abc&b=123")
spans = self.sorted_spans(self.memory_exporter.get_finished_spans())
self.assertEqual(len(spans), 2)
server_span = spans[0]
self.assertEqual(server_span.kind, SpanKind.SERVER)
self.assertSpanHasAttributes(
server_span, {"uri": "/pong?q=abc&b=123", "query": "q=abc&b=123"}
)
self.memory_exporter.clear()
def test_response_headers(self):
orig = get_global_response_propagator()
set_global_response_propagator(TraceResponsePropagator())
response = self.fetch("/")
spans = self.sorted_spans(self.memory_exporter.get_finished_spans())
self.assertEqual(len(spans), 3)
self.assertTraceResponseHeaderMatchesSpan(response.headers, spans[1])
set_global_response_propagator(orig)
def test_credential_removal(self):
app = HttpServerMock("test_credential_removal")
@app.route("/status/200")
def index():
return "hello"
with app.run("localhost", 5000):
response = self.fetch(
"http://username:password@localhost:5000/status/200"
)
self.assertEqual(response.code, 200)
spans = self.sorted_spans(self.memory_exporter.get_finished_spans())
self.assertEqual(len(spans), 1)
client = spans[0]
self.assertEqual(client.name, "GET")
self.assertEqual(client.kind, SpanKind.CLIENT)
self.assertSpanHasAttributes(
client,
{
HTTP_URL: "http://localhost:5000/status/200",
HTTP_METHOD: "GET",
HTTP_STATUS_CODE: 200,
},
)
self.memory_exporter.clear()
class TestTornadoInstrumentationWithXHeaders(TornadoTest):
def get_httpserver_options(self): # pylint: disable=no-self-use
return {"xheaders": True}
def test_xheaders(self):
response = self.fetch("/", headers={"X-Forwarded-For": "12.34.56.78"})
self.assertEqual(response.code, 201)
spans = self.get_finished_spans()
self.assertSpanHasAttributes(
spans.by_name("GET /"),
{
HTTP_METHOD: "GET",
HTTP_SCHEME: "http",
HTTP_HOST: "127.0.0.1:" + str(self.get_http_port()),
HTTP_TARGET: "/",
HTTP_CLIENT_IP: "12.34.56.78",
HTTP_STATUS_CODE: 201,
NET_PEER_IP: "127.0.0.1",
"tornado.handler": "tests.tornado_test_app.MainHandler",
},
)
class TornadoHookTest(TornadoTest):
_client_request_hook = None
_client_response_hook = None
_server_request_hook = None
def client_request_hook(self, span, handler):
if self._client_request_hook is not None:
self._client_request_hook(span, handler)
def client_response_hook(self, span, handler):
if self._client_response_hook is not None:
self._client_response_hook(span, handler)
def server_request_hook(self, span, handler):
if self._server_request_hook is not None:
self._server_request_hook(span, handler)
def test_hooks(self):
def server_request_hook(span, handler):
span.update_name("name from server hook")
handler.set_header("hello", "world")
def client_request_hook(span, request):
span.update_name("name from client hook")
def client_response_hook(span, response):
span.set_attribute("attr-from-hook", "value")
self._server_request_hook = server_request_hook
self._client_request_hook = client_request_hook
self._client_response_hook = client_response_hook
response = self.fetch("/")
self.assertEqual(response.headers.get("hello"), "world")
spans = self.sorted_spans(self.memory_exporter.get_finished_spans())
self.assertEqual(len(spans), 3)
server_span = spans[1]
self.assertEqual(server_span.kind, SpanKind.SERVER)
self.assertEqual(server_span.name, "name from server hook")
self.assertSpanHasAttributes(server_span, {"uri": "/"})
self.memory_exporter.clear()
client_span = spans[2]
self.assertEqual(client_span.kind, SpanKind.CLIENT)
self.assertEqual(client_span.name, "name from client hook")
self.assertSpanHasAttributes(client_span, {"attr-from-hook": "value"})
self.memory_exporter.clear()
class TestTornadoHTTPClientInstrumentation(TornadoTest, WsgiTestBase):
def test_http_client_success_response(self):
response = self.fetch("/")
self.assertEqual(response.code, 201)
spans = self.memory_exporter.get_finished_spans()
self.assertEqual(len(spans), 3)
manual, server, client = self.sorted_spans(spans)
self.assertEqual(manual.name, "manual")
self.assertEqual(server.name, "GET /")
self.assertEqual(client.name, "GET")
self.assertEqual(client.status.status_code, StatusCode.UNSET)
self.memory_exporter.clear()
def test_http_client_failed_response(self):
# when an exception isn't thrown
response = self.fetch("/some-404")
self.assertEqual(response.code, 404)
spans = self.memory_exporter.get_finished_spans()
self.assertEqual(len(spans), 2)
server, client = self.sorted_spans(spans)
self.assertEqual(server.name, "GET /some-404")
self.assertEqual(client.name, "GET")
self.assertEqual(client.status.status_code, StatusCode.ERROR)
self.memory_exporter.clear()
# when an exception is thrown
try:
response = self.fetch("/some-404", raise_error=True)
self.assertEqual(response.code, 404)
except HTTPClientError:
pass # expected exception - continue
spans = self.memory_exporter.get_finished_spans()
self.assertEqual(len(spans), 2)
server, client = self.sorted_spans(spans)
self.assertEqual(server.name, "GET /some-404")
self.assertEqual(client.name, "GET")
self.assertEqual(client.status.status_code, StatusCode.ERROR)
self.memory_exporter.clear()
class TestTornadoUninstrument(TornadoTest):
def test_uninstrument(self):
response = self.fetch("/")
self.assertEqual(response.code, 201)
spans = self.memory_exporter.get_finished_spans()
self.assertEqual(len(spans), 3)
manual, server, client = self.sorted_spans(spans)
self.assertEqual(manual.name, "manual")
self.assertEqual(server.name, "GET /")
self.assertEqual(client.name, "GET")
self.memory_exporter.clear()
TornadoInstrumentor().uninstrument()
response = self.fetch("/")
self.assertEqual(response.code, 201)
spans = self.memory_exporter.get_finished_spans()
self.assertEqual(len(spans), 1)
manual = spans[0]
self.assertEqual(manual.name, "manual")
class TestTornadoWrappedWithOtherFramework(TornadoTest):
def get_app(self):
tracer = trace.get_tracer(__name__)
app = make_app(tracer)
def middleware(request):
"""Wraps the request with a server span"""
with tracer.start_as_current_span(
"test", kind=trace.SpanKind.SERVER
):
app(request)
return middleware
def test_mark_span_internal_in_presence_of_another_span(self):
response = self.fetch("/")
self.assertEqual(response.code, 201)
spans = self.sorted_spans(self.memory_exporter.get_finished_spans())
self.assertEqual(len(spans), 4)
tornado_handler_span = spans[1]
self.assertEqual(trace.SpanKind.INTERNAL, tornado_handler_span.kind)
test_span = spans[2]
self.assertEqual(trace.SpanKind.SERVER, test_span.kind)
self.assertEqual(
test_span.context.span_id, tornado_handler_span.parent.span_id
)
class TestTornadoCustomRequestResponseHeadersAddedWithServerSpan(TornadoTest):
@patch.dict(
"os.environ",
{
OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST: "Custom-Test-Header-1,Custom-Test-Header-2,Custom-Test-Header-3"
},
)
def test_custom_request_headers_added_in_server_span(self):
headers = {
"Custom-Test-Header-1": "Test Value 1",
"Custom-Test-Header-2": "TestValue2,TestValue3",
}
response = self.fetch("/", headers=headers)
self.assertEqual(response.code, 201)
_, tornado_span, _ = self.sorted_spans(
self.memory_exporter.get_finished_spans()
)
expected = {
"http.request.header.custom_test_header_1": ("Test Value 1",),
"http.request.header.custom_test_header_2": (
"TestValue2,TestValue3",
),
}
self.assertEqual(tornado_span.kind, trace.SpanKind.SERVER)
self.assertSpanHasAttributes(tornado_span, expected)
@patch.dict(
"os.environ",
{
OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE: "content-type,content-length,my-custom-header,invalid-header"
},
)
def test_custom_response_headers_added_in_server_span(self):
response = self.fetch("/test_custom_response_headers")
self.assertEqual(response.code, 200)
tornado_span, _ = self.sorted_spans(
self.memory_exporter.get_finished_spans()
)
expected = {
"http.response.header.content_type": (
"text/plain; charset=utf-8",
),
"http.response.header.content_length": ("0",),
"http.response.header.my_custom_header": (
"my-custom-value-1,my-custom-header-2",
),
}
self.assertEqual(tornado_span.kind, trace.SpanKind.SERVER)
self.assertSpanHasAttributes(tornado_span, expected)
class TestTornadoCustomRequestResponseHeadersNotAddedWithInternalSpan(
TornadoTest
):
def get_app(self):
tracer = trace.get_tracer(__name__)
app = make_app(tracer)
def middleware(request):
"""Wraps the request with a server span"""
with tracer.start_as_current_span(
"test", kind=trace.SpanKind.SERVER
):
app(request)
return middleware
@patch.dict(
"os.environ",
{
OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST: "Custom-Test-Header-1,Custom-Test-Header-2,Custom-Test-Header-3"
},
)
def test_custom_request_headers_not_added_in_internal_span(self):
headers = {
"Custom-Test-Header-1": "Test Value 1",
"Custom-Test-Header-2": "TestValue2,TestValue3",
}
response = self.fetch("/", headers=headers)
self.assertEqual(response.code, 201)
_, tornado_span, _, _ = self.sorted_spans(
self.memory_exporter.get_finished_spans()
)
not_expected = {
"http.request.header.custom_test_header_1": ("Test Value 1",),
"http.request.header.custom_test_header_2": (
"TestValue2,TestValue3",
),
}
self.assertEqual(tornado_span.kind, trace.SpanKind.INTERNAL)
for key, _ in not_expected.items():
self.assertNotIn(key, tornado_span.attributes)
@patch.dict(
"os.environ",
{
OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE: "content-type,content-length,my-custom-header,invalid-header"
},
)
def test_custom_response_headers_not_added_in_internal_span(self):
response = self.fetch("/test_custom_response_headers")
self.assertEqual(response.code, 200)
tornado_span, _, _ = self.sorted_spans(
self.memory_exporter.get_finished_spans()
)
not_expected = {
"http.response.header.content_type": (
"text/plain; charset=utf-8",
),
"http.response.header.content_length": ("0",),
"http.response.header.my_custom_header": (
"my-custom-value-1,my-custom-header-2",
),
}
self.assertEqual(tornado_span.kind, trace.SpanKind.INTERNAL)
for key, _ in not_expected.items():
self.assertNotIn(key, tornado_span.attributes)