Exemplo n.º 1
0
    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)
Exemplo n.º 2
0
    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
Exemplo n.º 3
0
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
Exemplo n.º 4
0
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
Exemplo n.º 5
0
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("")
Exemplo n.º 6
0
    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)
Exemplo n.º 7
0
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://")
Exemplo n.º 8
0
 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)
Exemplo n.º 9
0
 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)
Exemplo n.º 10
0
    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
Exemplo n.º 11
0
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"
Exemplo n.º 12
0
 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)
Exemplo n.º 13
0
 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)
Exemplo n.º 14
0
    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
Exemplo n.º 15
0
 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)