async def test_multiple_requests(tracer): with override_http_config("asgi", dict(trace_query_string=True)): app = TraceMiddleware(basic_app, tracer=tracer) async with httpx.AsyncClient(app=app) as client: responses = await asyncio.gather( client.get("http://testserver/", params={"sleep": True}), client.get("http://testserver/", params={"sleep": True}), ) assert len(responses) == 2 assert [r.status_code for r in responses] == [200] * 2 assert [r.text for r in responses] == ["sleep"] * 2 spans = tracer.writer.pop_traces() assert len(spans) == 2 assert len(spans[0]) == 1 assert len(spans[1]) == 1 r1_span = spans[0][0] assert r1_span.name == "asgi.request" assert r1_span.span_type == "web" assert r1_span.get_tag("http.method") == "GET" assert r1_span.get_tag("http.url") == "http://testserver/" assert r1_span.get_tag("http.query.string") == "sleep=true" r2_span = spans[0][0] assert r2_span.name == "asgi.request" assert r2_span.span_type == "web" assert r2_span.get_tag("http.method") == "GET" assert r2_span.get_tag("http.url") == "http://testserver/" assert r2_span.get_tag("http.query.string") == "sleep=true"
async def test_query_string(scope, tracer): with override_http_config("asgi", dict(trace_query_string=True)): app = TraceMiddleware(basic_app, tracer=tracer) scope["query_string"] = "foo=bar" instance = ApplicationCommunicator(app, scope) await instance.send_input({"type": "http.request", "body": b""}) response_start = await instance.receive_output(1) assert response_start == { "type": "http.response.start", "status": 200, "headers": [[b"Content-Type", b"text/plain"]], } response_body = await instance.receive_output(1) assert response_body == { "type": "http.response.body", "body": b"*", } spans = tracer.writer.pop_traces() assert len(spans) == 1 assert len(spans[0]) == 1 request_span = spans[0][0] assert request_span.name == "asgi.request" assert request_span.span_type == "web" assert request_span.error == 0 assert request_span.get_tag("http.status_code") == "200" _check_span_tags(scope, request_span)
async def test_distributed_tracing(scope, tracer): app = TraceMiddleware(basic_app, tracer=tracer) headers = [ (http_propagation.HTTP_HEADER_PARENT_ID.encode(), "1234".encode()), (http_propagation.HTTP_HEADER_TRACE_ID.encode(), "5678".encode()), ] scope["headers"] = headers instance = ApplicationCommunicator(app, scope) await instance.send_input({"type": "http.request", "body": b""}) response_start = await instance.receive_output(1) assert response_start == { "type": "http.response.start", "status": 200, "headers": [[b"Content-Type", b"text/plain"]], } response_body = await instance.receive_output(1) assert response_body == { "type": "http.response.body", "body": b"*", } spans = tracer.writer.pop_traces() assert len(spans) == 1 assert len(spans[0]) == 1 request_span = spans[0][0] assert request_span.name == "asgi.request" assert request_span.span_type == "web" assert request_span.parent_id == 1234 assert request_span.trace_id == 5678 assert request_span.error == 0 assert request_span.get_tag("http.status_code") == "200" _check_span_tags(scope, request_span)
async def test_double_callable_asgi(scope, tracer): app = TraceMiddleware(double_callable_app, tracer=tracer) instance = ApplicationCommunicator(app, scope) await instance.send_input({"type": "http.request", "body": b""}) response_start = await instance.receive_output(1) assert response_start == { "type": "http.response.start", "status": 200, "headers": [[b"Content-Type", b"text/plain"]], } response_body = await instance.receive_output(1) assert response_body == { "type": "http.response.body", "body": b"*", } spans = tracer.writer.pop_traces() assert len(spans) == 1 assert len(spans[0]) == 1 request_span = spans[0][0] assert request_span.name == "asgi.request" assert request_span.span_type == "web" assert request_span.error == 0 assert request_span.get_tag("http.status_code") == "200" _check_span_tags(scope, request_span)
def traced_get_asgi_application(django, pin, func, instance, args, kwargs): from ddtrace.contrib.asgi import TraceMiddleware def django_asgi_modifier(span, scope): span.name = "django.request" return TraceMiddleware(func(*args, **kwargs), integration_config=config.django, span_modifier=django_asgi_modifier)
async def test_asgi_500(scope, tracer): app = TraceMiddleware(error_handled_app, tracer=tracer) instance = ApplicationCommunicator(app, scope) await instance.send_input({"type": "http.request", "body": b""}) await instance.receive_output(1) spans = tracer.writer.pop_traces() assert len(spans) == 1 assert len(spans[0]) == 1 request_span = spans[0][0] assert request_span.name == "asgi.request" assert request_span.error == 1 assert request_span.get_tag("http.status_code") == "500"
async def test_asgi_error(scope, tracer): app = TraceMiddleware(error_app, tracer=tracer) instance = ApplicationCommunicator(app, scope) with pytest.raises(RuntimeError): await instance.send_input({"type": "http.request", "body": b""}) await instance.receive_output(1) spans = tracer.writer.pop_traces() assert len(spans) == 1 assert len(spans[0]) == 1 request_span = spans[0][0] assert request_span.name == "asgi.request" assert request_span.error == 1 assert request_span.get_tag("error.msg") == "Test" assert request_span.get_tag("error.type") == "builtins.RuntimeError" assert 'raise RuntimeError("Test")' in request_span.get_tag("error.stack") _check_span_tags(scope, request_span)
async def test_bad_headers(scope, tracer, test_spans): """ When headers can't be decoded The middleware should not raise """ app = TraceMiddleware(basic_app, tracer=tracer) headers = [(bytes.fromhex("c0"), "test")] scope["headers"] = headers instance = ApplicationCommunicator(app, scope) await instance.send_input({"type": "http.request", "body": b""}) response_start = await instance.receive_output(1) assert response_start == { "type": "http.response.start", "status": 200, "headers": [[b"Content-Type", b"text/plain"]], } response_body = await instance.receive_output(1) assert response_body == { "type": "http.response.body", "body": b"*", }
async def test_asgi_error_custom(scope, tracer, test_spans): def custom_handle_exception_span(exc, span): span.set_tag("http.status_code", 501) app = TraceMiddleware(error_app, tracer=tracer, handle_exception_span=custom_handle_exception_span) instance = ApplicationCommunicator(app, scope) with pytest.raises(RuntimeError): await instance.send_input({"type": "http.request", "body": b""}) await instance.receive_output(1) spans = test_spans.pop_traces() assert len(spans) == 1 assert len(spans[0]) == 1 request_span = spans[0][0] assert request_span.name == "asgi.request" assert request_span.span_type == "web" assert request_span.error == 1 assert request_span.get_tag("http.status_code") == "501" assert request_span.get_tag("error.msg") == "Test" assert request_span.get_tag("error.type") == "builtins.RuntimeError" assert 'raise RuntimeError("Test")' in request_span.get_tag("error.stack") _check_span_tags(scope, request_span)
from django.core.asgi import get_asgi_application from django.urls import re_path from ddtrace.contrib.asgi import TraceMiddleware application = get_asgi_application() async def simple_asgi_app(scope, receive, send): await send({ "type": "http.response.start", "status": 200, "headers": [(b"Content-Type", b"text/plain")] }) await send({ "type": "http.response.body", "body": b"Hello World. It's me simple asgi app" }) channels_application = ProtocolTypeRouter({ "http": AuthMiddlewareStack( URLRouter([ re_path(r"traced-simple-asgi-app/", TraceMiddleware(simple_asgi_app)), re_path(r"simple-asgi-app/", simple_asgi_app), re_path(r"", application), ]), ) })
from django.core.asgi import get_asgi_application from ddtrace.contrib.asgi import TraceMiddleware application = TraceMiddleware(get_asgi_application())
async def test_get_asgi_span(tracer, test_spans): async def test_app(scope, receive, send): message = await receive() if message.get("type") == "http.request": asgi_span = span_from_scope(scope) assert asgi_span is not None assert asgi_span.name == "asgi.request" await send({ "type": "http.response.start", "status": 200, "headers": [[b"Content-Type", b"text/plain"]] }) await send({"type": "http.response.body", "body": b""}) app = TraceMiddleware(test_app, tracer=tracer) async with httpx.AsyncClient(app=app) as client: response = await client.get("http://testserver/") assert response.status_code == 200 with override_http_config("asgi", dict(trace_query_string=True)): app = TraceMiddleware(test_app, tracer=tracer) async with httpx.AsyncClient(app=app) as client: response = await client.get("http://testserver/") assert response.status_code == 200 async def test_app(scope, receive, send): message = await receive() if message.get("type") == "http.request": root = tracer.current_root_span() assert root.name == "root" asgi_span = span_from_scope(scope) assert asgi_span is not None assert asgi_span.name == "asgi.request" await send({ "type": "http.response.start", "status": 200, "headers": [[b"Content-Type", b"text/plain"]] }) await send({"type": "http.response.body", "body": b""}) app = TraceMiddleware(test_app, tracer=tracer) async with httpx.AsyncClient(app=app) as client: with tracer.trace("root"): response = await client.get("http://testserver/") assert response.status_code == 200 async def test_app_no_middleware(scope, receive, send): message = await receive() if message.get("type") == "http.request": asgi_span = span_from_scope(scope) assert asgi_span is None await send({ "type": "http.response.start", "status": 200, "headers": [[b"Content-Type", b"text/plain"]] }) await send({"type": "http.response.body", "body": b""}) async with httpx.AsyncClient(app=test_app_no_middleware) as client: response = await client.get("http://testserver/") assert response.status_code == 200
from starlette.applications import Starlette from starlette.responses import JSONResponse from starlette.routing import Route from ddtrace.contrib.asgi import TraceMiddleware # import the middleware async def homepage(request): return JSONResponse({'hello': 'world'}) routes = [Route("/", endpoint=homepage)] app = Starlette(debug=True, routes=routes) # wrap the app for tracing app = TraceMiddleware(app)
import os import django from channels.routing import get_default_application from ddtrace.contrib.asgi import TraceMiddleware os.environ.setdefault("DJANGO_SETTINGS_MODULE", "crossroads.settings.prod") django.setup() application = TraceMiddleware(get_default_application())