async def call(self, module, method, wrapped, instance, args, kwargs): if method == "Cursor.execute": query = args[0] name = extract_signature(query) # Truncate sql_string to 10000 characters to prevent large queries from # causing an error to APM server. query = shorten(query, string_length=10000) context = { "db": { "type": "sql", "statement": query }, "destination": { "address": instance.connection.host, "port": instance.connection.port, "service": { "name": "mysql", "resource": "mysql", "type": "db" }, }, } action = "query" else: raise AssertionError("call from uninstrumented method") async with async_capture_span(name, leaf=True, span_type="db", span_subtype="mysql", span_action=action, extra=context): return await wrapped(*args, **kwargs)
async def call(self, module, method, wrapped, instance, args, kwargs): request = kwargs.get("request", args[0]) request_method = request.method.upper() url = str(request.url) name = "{request_method} {host}".format(request_method=request_method, host=get_host_from_url(url)) url = sanitize_url(url) destination = url_to_destination(url) async with async_capture_span( name, span_type="external", span_subtype="http", extra={ "http": { "url": url }, "destination": destination }, leaf=True, ) as span: response = await wrapped(*args, **kwargs) if response is not None: if span.context: span.context["http"]["status_code"] = response.status_code span.set_success( ) if response.status_code < 400 else span.set_failure() return response
async def test_httpx_instrumentation_string_url(instrument, elasticapm_client, waiting_httpserver): waiting_httpserver.serve_content("") url = waiting_httpserver.url + "/hello_world" elasticapm_client.begin_transaction("transaction.test") async with async_capture_span("test_request", "test"): async with httpx.AsyncClient() as client: await client.get(url, allow_redirects=False) elasticapm_client.end_transaction("MyView") transactions = elasticapm_client.events[TRANSACTION] spans = elasticapm_client.spans_for_transaction(transactions[0]) assert spans[0]["name"].startswith("GET 127.0.0.1:") assert url == spans[0]["context"]["http"]["url"] headers = waiting_httpserver.requests[0].headers assert constants.TRACEPARENT_HEADER_NAME in headers trace_parent = TraceParent.from_string( headers[constants.TRACEPARENT_HEADER_NAME], tracestate_string=headers[constants.TRACESTATE_HEADER_NAME]) assert trace_parent.trace_id == transactions[0]["trace_id"] # Check that sample_rate was correctly placed in the tracestate assert constants.TRACESTATE.SAMPLE_RATE in trace_parent.tracestate_dict # this should be the span id of `httpx`, not of urllib3 assert trace_parent.span_id == spans[0]["id"] assert trace_parent.trace_options.recorded
async def test_httpx_instrumentation(instrument, elasticapm_client, waiting_httpserver): waiting_httpserver.serve_content("") url = waiting_httpserver.url + "/hello_world" parsed_url = compat.urlparse.urlparse(url) elasticapm_client.begin_transaction("transaction.test") async with async_capture_span("test_request", "test"): async with httpx.AsyncClient() as client: await client.get(url, allow_redirects=False) elasticapm_client.end_transaction("MyView") transactions = elasticapm_client.events[TRANSACTION] spans = elasticapm_client.spans_for_transaction(transactions[0]) assert spans[0]["name"].startswith("GET 127.0.0.1:") assert spans[0]["type"] == "external" assert spans[0]["subtype"] == "http" assert spans[0]["outcome"] == "success" assert url == spans[0]["context"]["http"]["url"] assert spans[0]["context"]["destination"]["service"] == { "name": "http://127.0.0.1:%d" % parsed_url.port, "resource": "127.0.0.1:%d" % parsed_url.port, "type": "external", } assert constants.TRACEPARENT_HEADER_NAME in waiting_httpserver.requests[ 0].headers trace_parent = TraceParent.from_string( waiting_httpserver.requests[0].headers[ constants.TRACEPARENT_HEADER_NAME]) assert trace_parent.trace_id == transactions[0]["trace_id"] # this should be the span id of `httpx`, not of urllib3 assert trace_parent.span_id == spans[0]["id"] assert trace_parent.trace_options.recorded
async def test_httpx_instrumentation_malformed_empty(instrument, elasticapm_client): try: from httpx._exceptions import UnsupportedProtocol except ImportError: pytest.skip("Test requires HTTPX 0.14+") elasticapm_client.begin_transaction("transaction.test") async with async_capture_span("test_request", "test"): with pytest.raises(UnsupportedProtocol): httpx.get("")
def call(self, module, method, wrapped, instance, args, kwargs): wrapped_name = self.get_wrapped_name(wrapped, instance, method) with async_capture_span( wrapped_name, span_type="db", span_subtype="redis", span_action="query", leaf=True ) as span: span.context["destination"] = _get_destination_info(instance) return wrapped(*args, **kwargs)
async def test_httpx_instrumentation_malformed_path(instrument, elasticapm_client): try: from httpx._exceptions import LocalProtocolError except ImportError: pytest.skip("Test requires HTTPX 0.14+") elasticapm_client.begin_transaction("transaction.test") async with async_capture_span("test_request", "test"): async with httpx.AsyncClient() as client: with pytest.raises(LocalProtocolError): await client.get("http://")
async def call(self, module, method, wrapped, instance, args, kwargs): query = args[0] if len(args) else kwargs["query"] name = extract_signature(query) context = {"db": {"type": "sql", "statement": query}} action = "query" async with async_capture_span(name, leaf=True, span_type="db", span_subtype="postgres", span_action=action, extra=context): return await wrapped(*args, **kwargs)
async def call(self, module, method, wrapped, instance, args, kwargs): if method == "Cursor.execute": query = args[0] if len(args) else kwargs["operation"] query = _bake_sql(instance.raw, query) name = extract_signature(query) context = {"db": {"type": "sql", "statement": query}} action = "query" elif method == "Cursor.callproc": func = args[0] if len(args) else kwargs["procname"] name = func + "()" context = None action = "exec" else: raise AssertionError("call from uninstrumented method") async with async_capture_span( name, leaf=True, span_type="db", span_subtype="postgres", span_action=action, extra=context ): return await wrapped(*args, **kwargs)
async def call(self, module, method, wrapped, instance, args, kwargs): url, method, headers = utils.get_request_data(args, kwargs) scheme, host, port, target = url if port != default_ports.get(scheme): host += ":" + str(port) signature = "%s %s" % (method.upper(), host) url = "%s://%s%s" % (scheme, host, url) transaction = execution_context.get_transaction() async with async_capture_span( signature, span_type="external", span_subtype="http", extra={"http": {"url": url}}, leaf=True, ) as span: # if httpcore has been called in a leaf span, this span might be a DroppedSpan. leaf_span = span while isinstance(leaf_span, DroppedSpan): leaf_span = leaf_span.parent if headers is not None: # It's possible that there are only dropped spans, e.g. if we started dropping spans due to the # transaction_max_spans limit. In this case, the transaction.id is used parent_id = leaf_span.id if leaf_span else transaction.id trace_parent = transaction.trace_parent.copy_from( span_id=parent_id, trace_options=TracingOptions(recorded=True) ) utils.set_disttracing_headers(headers, trace_parent, transaction) response = await wrapped(*args, **kwargs) status_code = utils.get_status(response) if status_code: if span.context: span.context["http"]["status_code"] = status_code span.set_success() if status_code < 400 else span.set_failure() return response
async def test_httpx_error(instrument, elasticapm_client, waiting_httpserver, status_code): waiting_httpserver.serve_content("", code=status_code) url = waiting_httpserver.url + "/hello_world" parsed_url = compat.urlparse.urlparse(url) elasticapm_client.begin_transaction("transaction") expected_sig = "GET {0}".format(parsed_url.netloc) url = "http://{0}/hello_world".format(parsed_url.netloc) async with async_capture_span("test_name", "test_type"): async with httpx.AsyncClient() as client: await client.get(url, allow_redirects=False) elasticapm_client.end_transaction("MyView") transactions = elasticapm_client.events[TRANSACTION] spans = elasticapm_client.spans_for_transaction(transactions[0]) assert spans[0]["name"] == expected_sig assert spans[0]["type"] == "external" assert spans[0]["subtype"] == "http" assert spans[0]["context"]["http"]["url"] == url assert spans[0]["context"]["http"]["status_code"] == status_code assert spans[0]["outcome"] == "failure"
async def call(self, module, method, wrapped, instance, args, kwargs): query = args[0] if len(args) else kwargs["query"] name = extract_signature(query) context = {"db": {"type": "sql", "statement": query}} action = "query" destination_info = { "address": kwargs.get("host", "localhost"), "port": int(kwargs.get("port", default_ports.get("postgresql"))), "service": { "name": "postgres", "resource": "postgres", "type": "db" }, } context["destination"] = destination_info async with async_capture_span(name, leaf=True, span_type="db", span_subtype="postgres", span_action=action, extra=context): return await wrapped(*args, **kwargs)
async def call(self, module, method, wrapped, instance, args, kwargs): query = self.get_query(method, args) name = extract_signature(query) sql_string = shorten(query, string_length=10000) context = {"db": {"type": "sql", "statement": sql_string}} action = "query" destination_info = { "address": kwargs.get("host", "localhost"), "port": int(kwargs.get("port", default_ports.get("postgresql"))), "service": { "name": "", "resource": "postgresql", "type": "" }, } context["destination"] = destination_info async with async_capture_span(name, leaf=True, span_type="db", span_subtype="postgresql", span_action=action, extra=context): return await wrapped(*args, **kwargs)
async def call(self, module, method, wrapped, instance, args, kwargs): if "method" in kwargs: method = kwargs["method"].decode("utf-8") else: method = args[0].decode("utf-8") # URL is a tuple of (scheme, host, port, path), we want path if "url" in kwargs: url = kwargs["url"][3].decode("utf-8") else: url = args[1][3].decode("utf-8") headers = None if "headers" in kwargs: headers = kwargs["headers"] if headers is None: headers = [] kwargs["headers"] = headers scheme, host, port = instance.origin scheme = scheme.decode("utf-8") host = host.decode("utf-8") if port != default_ports.get(scheme): host += ":" + str(port) signature = "%s %s" % (method.upper(), host) url = "%s://%s%s" % (scheme, host, url) destination = url_to_destination(url) transaction = execution_context.get_transaction() async with async_capture_span( signature, span_type="external", span_subtype="http", extra={ "http": { "url": url }, "destination": destination }, leaf=True, ) as span: # if httpcore has been called in a leaf span, this span might be a DroppedSpan. leaf_span = span while isinstance(leaf_span, DroppedSpan): leaf_span = leaf_span.parent if headers is not None: # It's possible that there are only dropped spans, e.g. if we started dropping spans due to the # transaction_max_spans limit. In this case, the transaction.id is used parent_id = leaf_span.id if leaf_span else transaction.id trace_parent = transaction.trace_parent.copy_from( span_id=parent_id, trace_options=TracingOptions(recorded=True)) self._set_disttracing_headers(headers, trace_parent, transaction) response = await wrapped(*args, **kwargs) if len(response) > 4: # httpcore < 0.11.0 # response = (http_version, status_code, reason_phrase, headers, stream) status_code = response[1] else: # httpcore >= 0.11.0 # response = (status_code, headers, stream, ext) status_code = response[0] if status_code: if span.context: span.context["http"]["status_code"] = status_code span.set_success() if status_code < 400 else span.set_failure() return response
async def call(self, module, method, wrapped, instance, args, kwargs): async with async_capture_span("asyncio.sleep", leaf=True, span_type="task", span_subtype="sleep"): return await wrapped(*args, **kwargs)