def call(self, module, method, wrapped, instance, args, kwargs): cls_name, method_name = method.split(".", 1) signature = ".".join([instance.full_name, method_name]) host, port = instance.database.client.address destination_info = { "address": host, "port": port, "service": { "name": "mongodb", "resource": "mongodb", "type": "db" }, } with capture_span( signature, span_type="db", span_subtype="mongodb", span_action="query", leaf=True, extra={"destination": destination_info}, ): return wrapped(*args, **kwargs)
def call(self, module, method, wrapped, instance, args, kwargs): if "operation_name" in kwargs: operation_name = kwargs["operation_name"] else: operation_name = args[0] target_endpoint = instance._endpoint.host parsed_url = urlparse.urlparse(target_endpoint) if "." in parsed_url.hostname: service, region = parsed_url.hostname.split(".", 2)[:2] else: service, region = parsed_url.hostname, None signature = "{}:{}".format(service, operation_name) extra_data = { "service": service, "region": region, "operation": operation_name } with capture_span(signature, "ext.http.aws", extra_data, leaf=True): return wrapped(*args, **kwargs)
def _trace_sql(self, method, sql, params, action=QUERY_ACTION): sql_string = self._bake_sql(sql) if action == EXEC_ACTION: signature = sql_string + "()" else: signature = self.extract_signature(sql_string) with capture_span( signature, span_type="db", span_subtype=self.provider_name, span_action=action, extra={"db": {"type": "sql", "statement": sql_string}, "destination": self._self_destination_info}, skip_frames=1, ) as span: if params is None: result = method(sql) else: result = method(sql, params) # store "rows affected", but only for DML queries like insert/update/delete if span and self.rowcount not in (-1, None) and signature.startswith(self.DML_QUERIES): span.update_context("db", {"rows_affected": self.rowcount}) return result
def test_requests_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") with capture_span("test_request", "test"): requests.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 url == spans[0]["context"]["http"]["url"] assert 200 == spans[0]["context"]["http"]["status_code"] assert spans[0]["context"]["destination"]["service"] == { "name": "", "resource": "127.0.0.1:%d" % parsed_url.port, "type": "", } assert spans[0]["outcome"] == "success" 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], tracestate_string=waiting_httpserver.requests[0].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 `requests`, not of urllib3 assert trace_parent.span_id == spans[0]["id"] assert trace_parent.trace_options.recorded
def call(self, module, method, wrapped, instance, args, kwargs): if 'operation_name' in kwargs: operation_name = kwargs['operation_name'] else: operation_name = args[0] target_endpoint = instance._endpoint.host parsed_url = urlparse.urlparse(target_endpoint) if '.' in parsed_url.hostname: service, region = parsed_url.hostname.split('.', 2)[:2] else: service, region = parsed_url.hostname, None signature = '{}:{}'.format(service, operation_name) extra_data = { 'service': service, 'region': region, 'operation': operation_name, } with capture_span(signature, 'ext.http.aws', extra_data, leaf=True): return wrapped(*args, **kwargs)
def call(self, module, method, wrapped, instance, args, kwargs): name = self.get_wrapped_name(wrapped, instance, method) address, port = None, None if instance.servers: first_server = instance.servers[0] if first_server.family == socket.AF_UNIX: address = first_server.address else: address, port = first_server.address destination = { "address": address, "port": port, } with capture_span( name, span_type="cache", span_subtype="memcached", span_action="query", extra={"destination": destination}, leaf=True, ): return wrapped(*args, **kwargs)
def test_requests_instrumentation_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.test") with capture_span("test_request", "test"): requests.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 url == spans[0]["context"]["http"]["url"] assert status_code == spans[0]["context"]["http"]["status_code"] 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 spans[0]["outcome"] == "failure"
def call(self, module, method, wrapped, instance, args, kwargs): name = self.get_wrapped_name(wrapped, instance, method) context = None if method == "Cluster.connect": span_type = "db.cassandra.connect" else: span_type = "db.cassandra.query" query = args[0] if args else kwargs.get("query") if hasattr(query, "query_string"): query_str = query.query_string elif hasattr(query, "prepared_statement") and hasattr(query.prepared_statement, "query"): query_str = query.prepared_statement.query elif isinstance(query, compat.string_types): query_str = query else: query_str = None if query_str: name = extract_signature(query_str) context = {"db": {"type": "sql", "statement": query_str}} with capture_span(name, span_type, context): return wrapped(*args, **kwargs)
def test_pylibmc(instrument, elasticapm_client): elasticapm_client.begin_transaction("transaction.test") with capture_span("test_memcached", "test"): host = os.environ.get("MEMCACHED_HOST", "localhost") conn = pylibmc.Client([host + ":11211"]) conn.set("mykey", "a") assert "a" == conn.get("mykey") assert {"mykey": "a"} == conn.get_multi(["mykey", "myotherkey"]) elasticapm_client.end_transaction("BillingView") transactions = elasticapm_client.events[TRANSACTION] spans = elasticapm_client.spans_for_transaction(transactions[0]) expected_signatures = {"test_memcached", "Client.set", "Client.get", "Client.get_multi"} assert {t["name"] for t in spans} == expected_signatures assert spans[0]["name"] == "Client.set" assert spans[0]["type"] == "cache" assert spans[0]["subtype"] == "memcached" assert spans[0]["action"] == "query" assert spans[0]["parent_id"] == spans[3]["id"] assert spans[1]["name"] == "Client.get" assert spans[1]["type"] == "cache" assert spans[1]["subtype"] == "memcached" assert spans[1]["action"] == "query" assert spans[1]["parent_id"] == spans[3]["id"] assert spans[2]["name"] == "Client.get_multi" assert spans[2]["type"] == "cache" assert spans[2]["subtype"] == "memcached" assert spans[2]["action"] == "query" assert spans[2]["parent_id"] == spans[3]["id"] assert spans[3]["name"] == "test_memcached" assert spans[3]["type"] == "test" assert len(spans) == 4
def test_urllib3(instrument, elasticapm_client, waiting_httpserver): waiting_httpserver.serve_content("") url = waiting_httpserver.url + "/hello_world" parsed_url = urlparse.urlparse(url) elasticapm_client.begin_transaction("transaction") expected_sig = "GET {0}".format(parsed_url.netloc) with capture_span("test_name", "test_type"): pool = urllib3.PoolManager(timeout=0.1) url = "http://{0}/hello_world".format(parsed_url.netloc) r = pool.request("GET", url) elasticapm_client.end_transaction("MyView") transactions = elasticapm_client.events[TRANSACTION] spans = elasticapm_client.spans_for_transaction(transactions[0]) expected_signatures = {"test_name", expected_sig} assert {t["name"] for t in spans} == expected_signatures assert len(spans) == 2 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"] == 200 assert spans[0]["context"]["destination"]["service"] == { "name": "", "resource": "127.0.0.1:%d" % parsed_url.port, "type": "", } assert spans[0]["parent_id"] == spans[1]["id"] assert spans[0]["outcome"] == "success" assert spans[1]["name"] == "test_name" assert spans[1]["type"] == "test_type" assert spans[1]["parent_id"] == transactions[0]["id"]
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) with capture_span("test_name", "test_type"): c = httpx.Client() c.get(url, **allow_redirects) 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"
def call(self, module, method, wrapped, instance, args, kwargs): args, kwargs, params = self._ensure_headers_in_kwargs(args, kwargs) signature = params.get("method", "GET").upper() signature += " " + get_host_from_url(params["url"]) url = sanitize_url(params["url"]) destination = url_to_destination(url) transaction = execution_context.get_transaction() with capture_span( signature, span_type="external", span_subtype="http", extra={ "http": { "url": url }, "destination": destination }, leaf=True, ) as span: # if httplib2 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 # It's possible that there are only dropped spans, e.g. if we started dropping spans. # 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(params["headers"], trace_parent, transaction) response, content = wrapped(*args, **kwargs) if span.context: span.context["http"]["status_code"] = response.status span.set_success() if response.status < 400 else span.set_failure() return response, content
def call(self, module, method, wrapped, instance, args, kwargs): name = "GraphQL" info = "" query = args[2] if "ResolveInfo" == type(query).__name__: if str(query.return_type) in [ "Boolean", "Context", "Date", "DateTime", "Decimal", "Dynamic", "Float", "ID", "Int", "String", "Time", "UUID", "Boolean", "String", ]: return wrapped(*args, **kwargs) op = query.operation.operation field = query.field_name info = "%s %s" % (op, field) elif "RequestParams" == type(query).__name__: info = "%s %s" % ("request", query.query) else: info = str(query) with capture_span("%s.%s" % (name, info), span_type="external", span_subtype="graphql", span_action="query"): return wrapped(*args, **kwargs)
def call(self, module, method, wrapped, instance, args, kwargs): if "operation_name" in kwargs: operation_name = kwargs["operation_name"] else: operation_name = args[0] service_model = instance.meta.service_model if hasattr(service_model, "service_id"): # added in boto3 1.7 service = service_model.service_id else: service = service_model.service_name.upper() service = endpoint_to_service_id.get(service, service) parsed_url = urlparse.urlparse(instance.meta.endpoint_url) context = { "destination": { "address": parsed_url.hostname, "port": parsed_url.port, "cloud": {"region": instance.meta.region_name}, } } handler_info = None handler = handlers.get(service, False) if handler: handler_info = handler(operation_name, service, instance, args, kwargs, context) if not handler_info: handler_info = handle_default(operation_name, service, instance, args, kwargs, context) with capture_span( handler_info.signature, span_type=handler_info.span_type, leaf=True, span_subtype=handler_info.span_subtype, span_action=handler_info.span_action, extra=handler_info.context, ): return wrapped(*args, **kwargs)
def test_pymemcache_pooled_client(instrument, elasticapm_client): elasticapm_client.begin_transaction("transaction.test") host = os.environ.get("MEMCACHED_HOST", "localhost") with capture_span("test_pymemcache", "test"): conn = pymemcache.client.base.PooledClient((host, 11211)) conn.set("mykey", "a") assert b"a" == conn.get("mykey") assert {"mykey": b"a"} == conn.get_many(["mykey", "myotherkey"]) elasticapm_client.end_transaction("BillingView") transactions = elasticapm_client.events[TRANSACTION] spans = elasticapm_client.spans_for_transaction(transactions[0]) expected_signatures = { "test_pymemcache", "PooledClient.set", "PooledClient.get", "PooledClient.get_many", } assert {t["name"] for t in spans} == expected_signatures assert len(spans) == 4
def call(self, module, method, wrapped, instance, args, kwargs): request_object = args[1] if len(args) > 1 else kwargs["req"] method = request_object.get_method() host = request_host(request_object) url = sanitize_url(request_object.get_full_url()) signature = method.upper() + " " + host transaction = execution_context.get_transaction() with capture_span(signature, "ext.http.urllib", {"http": {"url": url}}, leaf=True) as span: # if urllib 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 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) ) request_object.add_header(constants.TRACEPARENT_HEADER_NAME, trace_parent.to_string()) return wrapped(*args, **kwargs)
def call(self, module, method, wrapped, instance, args, kwargs): # pylint: disable=too-many-arguments """Wrapped method call""" view = kwargs.get('view') context = kwargs.get('context') with capture_span('RENDER', span_type='template', span_subtype='chameleon', span_action='render', labels={ 'view': '{}.{}'.format(view.__class__.__module__, view.__class__.__name__) if view is not None else None, 'context': '{}.{}'.format(context.__class__.__module__, context.__class__.__name__) if context is not None else None, 'filename': instance.filename }, leaf=False): return wrapped(*args, **kwargs)
def test_pipeline(instrument, elasticapm_client, redis_conn): elasticapm_client.begin_transaction("transaction.test") with capture_span("test_pipeline", "test"): pipeline = redis_conn.pipeline() pipeline.rpush("mykey", "a", "b") pipeline.expire("mykey", 1000) pipeline.execute() elasticapm_client.end_transaction("MyView") transactions = elasticapm_client.events[TRANSACTION] spans = elasticapm_client.spans_for_transaction(transactions[0]) expected_signatures = {"test_pipeline", "StrictPipeline.execute"} assert {t["name"] for t in spans} == expected_signatures assert spans[0]["name"] == "StrictPipeline.execute" assert spans[0]["type"] == "cache.redis" assert spans[1]["name"] == "test_pipeline" assert spans[1]["type"] == "test" assert len(spans) == 2
def test_requests_instrumentation_via_prepared_request(instrument, elasticapm_client, waiting_httpserver): waiting_httpserver.serve_content("") url = waiting_httpserver.url + "/hello_world" elasticapm_client.begin_transaction("transaction.test") with capture_span("test_request", "test"): r = requests.Request("get", url) pr = r.prepare() s = requests.Session() s.send(pr, 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"] 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 `requests`, not of urllib3 assert trace_parent.span_id == spans[0]["id"] assert trace_parent.trace_options.recorded
def test_rq_patches_redis(instrument, elasticapm_client, redis_conn): # Let's go ahead and change how something important works redis_conn._pipeline = partial(StrictRedis.pipeline, redis_conn) elasticapm_client.begin_transaction("transaction.test") with capture_span("test_pipeline", "test"): # conn = redis.StrictRedis() pipeline = redis_conn._pipeline() pipeline.rpush("mykey", "a", "b") pipeline.expire("mykey", 1000) pipeline.execute() elasticapm_client.end_transaction("MyView") transactions = elasticapm_client.events[TRANSACTION] spans = elasticapm_client.spans_for_transaction(transactions[0]) assert spans[0]["name"] in ("StrictPipeline.execute", "Pipeline.execute") assert spans[0]["type"] == "cache.redis" assert spans[1]["name"] == "test_pipeline" assert spans[1]["type"] == "test" assert len(spans) == 2
def test_pipeline(elasticapm_client, redis_conn): elasticapm_client.begin_transaction("transaction.test") with capture_span("test_pipeline", "test"): pipeline = redis_conn.pipeline() pipeline.rpush("mykey", "a", "b") pipeline.expire("mykey", 1000) pipeline.execute() elasticapm_client.end_transaction("MyView") transactions = elasticapm_client.instrumentation_store.get_all() spans = transactions[0]['spans'] expected_signatures = {'test_pipeline', 'StrictPipeline.execute'} assert {t['name'] for t in spans} == expected_signatures assert spans[0]['name'] == 'StrictPipeline.execute' assert spans[0]['type'] == 'cache.redis' assert spans[1]['name'] == 'test_pipeline' assert spans[1]['type'] == 'test' assert len(spans) == 2
def call(self, module, method, wrapped, instance, args, kwargs): if "request" in kwargs: request = kwargs["request"] else: request = args[0] signature = request.method.upper() signature += " " + get_host_from_url(request.url) url = sanitize_url(request.url) with capture_span( signature, span_type="external", span_subtype="http", extra={"http": {"url": url}}, leaf=True, ) as span: response = wrapped(*args, **kwargs) # requests.Response objects are falsy if status code > 400, so we have to check for None instead 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
def test_redis_client(instrument, elasticapm_client, redis_conn): elasticapm_client.begin_transaction("transaction.test") with capture_span("test_redis_client", "test"): redis_conn.rpush("mykey", "a", "b") redis_conn.expire("mykey", 1000) elasticapm_client.end_transaction("MyView") transactions = elasticapm_client.events[TRANSACTION] spans = elasticapm_client.spans_for_transaction(transactions[0]) expected_signatures = {"test_redis_client", "RPUSH", "EXPIRE"} assert {t["name"] for t in spans} == expected_signatures assert spans[0]["name"] == "RPUSH" assert spans[0]["type"] == "cache.redis" assert spans[1]["name"] == "EXPIRE" assert spans[1]["type"] == "cache.redis" assert spans[2]["name"] == "test_redis_client" assert spans[2]["type"] == "test" assert len(spans) == 3
def test_redis_client(elasticapm_client, redis_conn): elasticapm_client.begin_transaction("transaction.test") with capture_span("test_redis_client", "test"): redis_conn.rpush("mykey", "a", "b") redis_conn.expire("mykey", 1000) elasticapm_client.end_transaction("MyView") transactions = elasticapm_client.instrumentation_store.get_all() spans = transactions[0]['spans'] expected_signatures = {'test_redis_client', 'RPUSH', 'EXPIRE'} assert {t['name'] for t in spans} == expected_signatures assert spans[0]['name'] == 'RPUSH' assert spans[0]['type'] == 'cache.redis' assert spans[1]['name'] == 'EXPIRE' assert spans[1]['type'] == 'cache.redis' assert spans[2]['name'] == 'test_redis_client' assert spans[2]['type'] == 'test' assert len(spans) == 3
def call(self, module, method, wrapped, instance, args, kwargs): if "method" in kwargs: method = kwargs["method"] else: method = args[0] host = instance.host if instance.port != default_ports.get(instance.scheme): host += ":" + str(instance.port) if "url" in kwargs: url = kwargs["url"] else: url = args[1] signature = method.upper() + " " + host url = instance.scheme + "://" + host + url with capture_span(signature, "ext.http.urllib3", {"url": url}, leaf=True): return wrapped(*args, **kwargs)
def call(self, module, method, wrapped, instance, args, kwargs): wrapped_name = self.get_wrapped_name(wrapped, instance, method) with capture_span(wrapped_name, "cache.redis", leaf=True): return wrapped(*args, **kwargs)
def call(self, module, method, wrapped, instance, args, kwargs): name = self.get_wrapped_name(wrapped, instance, method) with capture_span(name, "cache.memcached"): return wrapped(*args, **kwargs)
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() with 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. # 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 = 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
def test_requests_instrumentation_malformed_path(instrument, elasticapm_client): elasticapm_client.begin_transaction("transaction.test") with capture_span("test_request", "test"): with pytest.raises(InvalidURL): requests.get("http://")
def test_requests_instrumentation_malformed_schema(instrument, elasticapm_client): elasticapm_client.begin_transaction("transaction.test") with capture_span("test_request", "test"): with pytest.raises(MissingSchema): requests.get("")