def test_not_recording(self): mock_tracer = mock.Mock() mock_span = mock.Mock() mock_span.is_recording.return_value = False mock_tracer.start_span.return_value = mock_span with mock.patch("opentelemetry.trace.get_tracer"): # pylint: disable=W0612 host, port = self._http_request( trace_config=aiohttp_client.create_trace_config(), url="/test-path?query=param#foobar", ) 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_hooks(self): method = "PATCH" path = "/some/path" expected = "PATCH - /some/path" def request_hook(span: Span, params: aiohttp.TraceRequestStartParams): span.update_name(f"{params.method} - {params.url.path}") def response_hook( span: Span, params: typing.Union[ aiohttp.TraceRequestEndParams, aiohttp.TraceRequestExceptionParams, ], ): span.set_attribute("response_hook_attr", "value") host, port = self._http_request( trace_config=aiohttp_client.create_trace_config( request_hook=request_hook, response_hook=response_hook, ), method=method, url=path, status_code=HTTPStatus.OK, ) for span in self.memory_exporter.get_finished_spans(): self.assertEqual(span.name, expected) self.assertEqual( (span.status.status_code, span.status.description), (StatusCode.UNSET, None), ) self.assertEqual( span.attributes[SpanAttributes.HTTP_METHOD], method ) self.assertEqual( span.attributes[SpanAttributes.HTTP_URL], f"http://{host}:{port}{path}", ) self.assertEqual( span.attributes[SpanAttributes.HTTP_STATUS_CODE], HTTPStatus.OK ) self.assertIn("response_hook_attr", span.attributes) self.assertEqual(span.attributes["response_hook_attr"], "value") self.memory_exporter.clear()
def test_timeout(self): async def request_handler(request): await asyncio.sleep(1) assert "traceparent" in request.headers return aiohttp.web.Response() host, port = self._http_request( trace_config=aiohttp_client.create_trace_config(), url="/test_timeout", request_handler=request_handler, timeout=aiohttp.ClientTimeout(sock_read=0.01), ) self.assert_spans([( "HTTP GET", (StatusCode.ERROR, None), { "http.method": "GET", "http.url": "http://{}:{}/test_timeout".format(host, port), }, )])
def test_url_filter_option(self): # Strips all query params from URL before adding as a span attribute. def strip_query_params(url: yarl.URL) -> str: return str(url.with_query(None)) host, port = self._http_request( trace_config=aiohttp_client.create_trace_config( url_filter=strip_query_params), url="/some/path?query=param&other=param2", status_code=HTTPStatus.OK, ) self.assert_spans([( "HTTP GET", (StatusCode.UNSET, None), { "http.method": "GET", "http.url": "http://{}:{}/some/path".format(host, port), "http.status_code": int(HTTPStatus.OK), }, )])
def test_timeout(self): async def request_handler(request): await asyncio.sleep(1) assert "traceparent" in request.headers return aiohttp.web.Response() host, port = self._http_request( trace_config=aiohttp_client.create_trace_config(), url="/test_timeout", request_handler=request_handler, timeout=aiohttp.ClientTimeout(sock_read=0.01), ) self.assert_spans([( "HTTP GET", (StatusCode.ERROR, None), { SpanAttributes.HTTP_METHOD: "GET", SpanAttributes.HTTP_URL: f"http://{host}:{port}/test_timeout", }, )])
def test_url_filter_option(self): # Strips all query params from URL before adding as a span attribute. def strip_query_params(url: yarl.URL) -> str: return str(url.with_query(None)) host, port = self._http_request( trace_config=aiohttp_client.create_trace_config( url_filter=strip_query_params), url="/some/path?query=param&other=param2", status_code=HTTPStatus.OK, ) self.assert_spans([( "HTTP GET", (StatusCode.UNSET, None), { SpanAttributes.HTTP_METHOD: "GET", SpanAttributes.HTTP_URL: f"http://{host}:{port}/some/path", SpanAttributes.HTTP_STATUS_CODE: int(HTTPStatus.OK), }, )])
def test_span_name_option(self): for span_name, method, path, expected in ( ("static", "POST", "/static-span-name", "static"), ( lambda params: "{} - {}".format( params.method, params.url.path ), "PATCH", "/some/path", "PATCH - /some/path", ), ): with self.subTest(span_name=span_name, method=method, path=path): host, port = self._http_request( trace_config=aiohttp_client.create_trace_config( span_name=span_name ), method=method, url=path, status_code=HTTPStatus.OK, ) self.assert_spans( [ ( expected, (StatusCode.UNSET, None), { "component": "http", "http.method": method, "http.url": "http://{}:{}{}".format( host, port, path ), "http.status_code": int(HTTPStatus.OK), "http.status_text": HTTPStatus.OK.phrase, }, ) ] ) self.memory_exporter.clear()
def test_span_name_option(self): for span_name, method, path, expected in ( ("static", "POST", "/static-span-name", "static"), ( lambda params: "{} - {}".format( params.method, params.url.path ), "PATCH", "/some/path", "PATCH - /some/path", ), ): with self.subTest(span_name=span_name, method=method, path=path): host, port = self._http_request( trace_config=aiohttp_client.create_trace_config( span_name=span_name ), method=method, url=path, status_code=HTTPStatus.OK, ) self.assert_spans( [ ( expected, (StatusCode.UNSET, None), { SpanAttributes.HTTP_METHOD: method, SpanAttributes.HTTP_URL: "http://{}:{}{}".format( host, port, path ), SpanAttributes.HTTP_STATUS_CODE: int( HTTPStatus.OK ), }, ) ] ) self.memory_exporter.clear()
def _get_client( self, timeout_sec: t.Optional[float] = None, ) -> "ClientSession": import aiohttp if (self._loop is None or self._client is None or self._client.closed or self._loop.is_closed()): import yarl from opentelemetry.instrumentation.aiohttp_client import create_trace_config def strip_query_params(url: yarl.URL) -> str: return str(url.with_query(None)) jar = aiohttp.DummyCookieJar() if timeout_sec is not None: timeout = aiohttp.ClientTimeout(total=timeout_sec) else: DEFAULT_TIMEOUT = aiohttp.ClientTimeout(total=5 * 60) timeout = DEFAULT_TIMEOUT self._client = aiohttp.ClientSession( trace_configs=[ create_trace_config( # Remove all query params from the URL attribute on the span. url_filter=strip_query_params, tracer_provider=DeploymentContainer.tracer_provider. get(), ) ], connector=self._get_conn(), auto_decompress=False, cookie_jar=jar, connector_owner=False, timeout=timeout, loop=self._loop, ) return self._client
def test_too_many_redirects(self): async def request_handler(request): # Create a redirect loop. location = request.url assert "traceparent" in request.headers raise aiohttp.web.HTTPFound(location=location) host, port = self._http_request( trace_config=aiohttp_client.create_trace_config(), url="/test_too_many_redirects", request_handler=request_handler, max_redirects=2, ) self.assert_spans([( "HTTP GET", (StatusCode.ERROR, None), { "http.method": "GET", "http.url": "http://{}:{}/test_too_many_redirects".format(host, port), }, )])
def test_too_many_redirects(self): async def request_handler(request): # Create a redirect loop. location = request.url assert "traceparent" in request.headers raise aiohttp.web.HTTPFound(location=location) host, port = self._http_request( trace_config=aiohttp_client.create_trace_config(), url="/test_too_many_redirects", request_handler=request_handler, max_redirects=2, ) self.assert_spans([( "HTTP GET", (StatusCode.ERROR, None), { SpanAttributes.HTTP_METHOD: "GET", SpanAttributes.HTTP_URL: f"http://{host}:{port}/test_too_many_redirects", }, )])
def test_status_codes(self): for status_code, span_status in ( (HTTPStatus.OK, StatusCanonicalCode.OK), (HTTPStatus.TEMPORARY_REDIRECT, StatusCanonicalCode.OK), (HTTPStatus.SERVICE_UNAVAILABLE, StatusCanonicalCode.UNAVAILABLE), ( HTTPStatus.GATEWAY_TIMEOUT, StatusCanonicalCode.DEADLINE_EXCEEDED, ), ): with self.subTest(status_code=status_code): host, port = self._http_request( trace_config=aiohttp_client.create_trace_config(), url="/test-path?query=param#foobar", status_code=status_code, ) self.assert_spans([( "HTTP GET", (span_status, None), { "component": "http", "http.method": "GET", "http.url": "http://{}:{}/test-path?query=param#foobar".format( host, port), "http.status_code": int(status_code), "http.status_text": status_code.phrase, }, )]) self.memory_exporter.clear()