From ea165dd225e753b2740cdfb770dbd00a1e053707 Mon Sep 17 00:00:00 2001 From: dimitraparaskevopoulou Date: Thu, 10 Jun 2021 15:49:11 +0200 Subject: [PATCH 1/5] requests dependency changed ZD:20093, added async_tracer support for pymongo instrumentation --- instana/instrumentation/pymongo.py | 11 +- instana/version.py | 2 +- setup.py | 2 +- tests/clients/test_pymongo.py | 233 +++++++++++++++++++++++++++-- 4 files changed, 233 insertions(+), 15 deletions(-) diff --git a/instana/instrumentation/pymongo.py b/instana/instrumentation/pymongo.py index 5cd2752d..bac19364 100644 --- a/instana/instrumentation/pymongo.py +++ b/instana/instrumentation/pymongo.py @@ -4,7 +4,7 @@ from __future__ import absolute_import from ..log import logger -from ..singletons import tracer +from ..singletons import tracer, async_tracer try: import pymongo @@ -17,12 +17,15 @@ def __init__(self): def started(self, event): parent_span = tracer.active_span - + active_tracer = tracer # return early if we're not tracing if parent_span is None: - return + parent_span = async_tracer.active_span + if parent_span is None: + return + active_tracer = async_tracer - with tracer.start_active_span("mongo", child_of=parent_span) as scope: + with active_tracer.start_active_span("mongo", child_of=parent_span) as scope: self._collect_connection_tags(scope.span, event) self._collect_command_tags(scope.span, event) diff --git a/instana/version.py b/instana/version.py index 9c9cdb40..508aa20f 100644 --- a/instana/version.py +++ b/instana/version.py @@ -3,4 +3,4 @@ # Module version file. Used by setup.py and snapshot reporting. -VERSION = '1.33.1' +VERSION = '1.33.2' diff --git a/setup.py b/setup.py index 123ca06f..0bb33c1c 100644 --- a/setup.py +++ b/setup.py @@ -64,7 +64,7 @@ def check_setuptools(): 'certifi>=2018.4.16', 'fysom>=2.1.2', 'opentracing>=2.3.0', - 'requests>=2.8.0', + 'requests>=2.6.0', # requested to support 2.6.0 'six>=1.12.0', 'urllib3<1.26,>=1.21.1'], entry_points={ diff --git a/tests/clients/test_pymongo.py b/tests/clients/test_pymongo.py index 9cc9b2fe..316e667f 100644 --- a/tests/clients/test_pymongo.py +++ b/tests/clients/test_pymongo.py @@ -11,7 +11,7 @@ assert_false, assert_true, assert_list_equal) from ..helpers import testenv -from instana.singletons import tracer +from instana.singletons import tracer, async_tracer import pymongo import bson @@ -19,7 +19,7 @@ logger = logging.getLogger(__name__) -class TestPyMongo(unittest.TestCase): +class TestPyMongoTracer(unittest.TestCase): def setUp(self): self.conn = pymongo.MongoClient(host=testenv['mongodb_host'], port=int(testenv['mongodb_port']), username=testenv['mongodb_user'], password=testenv['mongodb_pw']) @@ -107,11 +107,11 @@ def test_successful_update_query(self): payload = json.loads(db_span.data["mongo"]["json"]) assert_true({ - "q": {"type": "string"}, - "u": {"$set": {"type": "int"}}, - "multi": False, - "upsert": False - } in payload, db_span.data["mongo"]["json"]) + "q": {"type": "string"}, + "u": {"$set": {"type": "int"}}, + "multi": False, + "upsert": False + } in payload, db_span.data["mongo"]["json"]) def test_successful_delete_query(self): with tracer.start_active_span("test"): @@ -174,7 +174,8 @@ def test_successful_map_reduce_query(self): reducer = "function (key, values) { return len(values); }" with tracer.start_active_span("test"): - self.conn.test.records.map_reduce(bson.code.Code(mapper), bson.code.Code(reducer), "results", query={"x": {"$lt": 2}}) + self.conn.test.records.map_reduce(bson.code.Code(mapper), bson.code.Code(reducer), "results", + query={"x": {"$lt": 2}}) assert_is_none(tracer.active_span) @@ -192,7 +193,8 @@ def test_successful_map_reduce_query(self): self.assertEqual(db_span.n, "mongo") self.assertEqual(db_span.data["mongo"]["service"], "%s:%s" % (testenv['mongodb_host'], testenv['mongodb_port'])) self.assertEqual(db_span.data["mongo"]["namespace"], "test.records") - self.assertEqual(db_span.data["mongo"]["command"].lower(), "mapreduce") # mapreduce command was renamed to mapReduce in pymongo 3.9.0 + self.assertEqual(db_span.data["mongo"]["command"].lower(), + "mapreduce") # mapreduce command was renamed to mapReduce in pymongo 3.9.0 self.assertEqual(db_span.data["mongo"]["filter"], '{"x": {"$lt": 2}}') assert_is_not_none(db_span.data["mongo"]["json"]) @@ -228,3 +230,216 @@ def test_successful_mutiple_queries(self): # ensure spans are ordered the same way as commands assert_list_equal(commands, ["insert", "update", "delete"]) + + +class TestPyMongoAsyncTracer(unittest.TestCase): + def setUp(self): + self.conn = pymongo.MongoClient(host=testenv['mongodb_host'], port=int(testenv['mongodb_port']), + username=testenv['mongodb_user'], password=testenv['mongodb_pw']) + self.conn.test.records.delete_many(filter={}) + + self.recorder = async_tracer.recorder + self.recorder.clear_spans() + + def tearDown(self): + return None + + def test_successful_find_query(self): + with async_tracer.start_active_span("test"): + self.conn.test.records.find_one({"type": "string"}) + + assert_is_none(async_tracer.active_span) + + spans = self.recorder.queued_spans() + self.assertEqual(len(spans), 2) + + db_span = spans[0] + test_span = spans[1] + + self.assertEqual(test_span.t, db_span.t) + self.assertEqual(db_span.p, test_span.s) + + assert_is_none(db_span.ec) + + self.assertEqual(db_span.n, "mongo") + self.assertEqual(db_span.data["mongo"]["service"], "%s:%s" % (testenv['mongodb_host'], testenv['mongodb_port'])) + self.assertEqual(db_span.data["mongo"]["namespace"], "test.records") + self.assertEqual(db_span.data["mongo"]["command"], "find") + + self.assertEqual(db_span.data["mongo"]["filter"], '{"type": "string"}') + assert_is_none(db_span.data["mongo"]["json"]) + + def test_successful_insert_query(self): + with async_tracer.start_active_span("test"): + self.conn.test.records.insert_one({"type": "string"}) + + assert_is_none(async_tracer.active_span) + + spans = self.recorder.queued_spans() + self.assertEqual(len(spans), 2) + + db_span = spans[0] + test_span = spans[1] + + self.assertEqual(test_span.t, db_span.t) + self.assertEqual(db_span.p, test_span.s) + + assert_is_none(db_span.ec) + + self.assertEqual(db_span.n, "mongo") + self.assertEqual(db_span.data["mongo"]["service"], "%s:%s" % (testenv['mongodb_host'], testenv['mongodb_port'])) + self.assertEqual(db_span.data["mongo"]["namespace"], "test.records") + self.assertEqual(db_span.data["mongo"]["command"], "insert") + + assert_is_none(db_span.data["mongo"]["filter"]) + + def test_successful_update_query(self): + with async_tracer.start_active_span("test"): + self.conn.test.records.update_one({"type": "string"}, {"$set": {"type": "int"}}) + + assert_is_none(async_tracer.active_span) + + spans = self.recorder.queued_spans() + self.assertEqual(len(spans), 2) + + db_span = spans[0] + test_span = spans[1] + + self.assertEqual(test_span.t, db_span.t) + self.assertEqual(db_span.p, test_span.s) + + assert_is_none(db_span.ec) + + self.assertEqual(db_span.n, "mongo") + self.assertEqual(db_span.data["mongo"]["service"], "%s:%s" % (testenv['mongodb_host'], testenv['mongodb_port'])) + self.assertEqual(db_span.data["mongo"]["namespace"], "test.records") + self.assertEqual(db_span.data["mongo"]["command"], "update") + + assert_is_none(db_span.data["mongo"]["filter"]) + assert_is_not_none(db_span.data["mongo"]["json"]) + + payload = json.loads(db_span.data["mongo"]["json"]) + assert_true({ + "q": {"type": "string"}, + "u": {"$set": {"type": "int"}}, + "multi": False, + "upsert": False + } in payload, db_span.data["mongo"]["json"]) + + def test_successful_delete_query(self): + with async_tracer.start_active_span("test"): + self.conn.test.records.delete_one(filter={"type": "string"}) + + assert_is_none(async_tracer.active_span) + + spans = self.recorder.queued_spans() + self.assertEqual(len(spans), 2) + + db_span = spans[0] + test_span = spans[1] + + self.assertEqual(test_span.t, db_span.t) + self.assertEqual(db_span.p, test_span.s) + + assert_is_none(db_span.ec) + + self.assertEqual(db_span.n, "mongo") + self.assertEqual(db_span.data["mongo"]["service"], "%s:%s" % (testenv['mongodb_host'], testenv['mongodb_port'])) + self.assertEqual(db_span.data["mongo"]["namespace"], "test.records") + self.assertEqual(db_span.data["mongo"]["command"], "delete") + + assert_is_none(db_span.data["mongo"]["filter"]) + assert_is_not_none(db_span.data["mongo"]["json"]) + + payload = json.loads(db_span.data["mongo"]["json"]) + assert_true({"q": {"type": "string"}, "limit": 1} in payload, db_span.data["mongo"]["json"]) + + def test_successful_aggregate_query(self): + with async_tracer.start_active_span("test"): + self.conn.test.records.count_documents({"type": "string"}) + + assert_is_none(async_tracer.active_span) + + spans = self.recorder.queued_spans() + self.assertEqual(len(spans), 2) + + db_span = spans[0] + test_span = spans[1] + + self.assertEqual(test_span.t, db_span.t) + self.assertEqual(db_span.p, test_span.s) + + assert_is_none(db_span.ec) + + self.assertEqual(db_span.n, "mongo") + self.assertEqual(db_span.data["mongo"]["service"], "%s:%s" % (testenv['mongodb_host'], testenv['mongodb_port'])) + self.assertEqual(db_span.data["mongo"]["namespace"], "test.records") + self.assertEqual(db_span.data["mongo"]["command"], "aggregate") + + assert_is_none(db_span.data["mongo"]["filter"]) + assert_is_not_none(db_span.data["mongo"]["json"]) + + payload = json.loads(db_span.data["mongo"]["json"]) + assert_true({"$match": {"type": "string"}} in payload, db_span.data["mongo"]["json"]) + + def test_successful_map_reduce_query(self): + mapper = "function () { this.tags.forEach(function(z) { emit(z, 1); }); }" + reducer = "function (key, values) { return len(values); }" + + with async_tracer.start_active_span("test"): + self.conn.test.records.map_reduce(bson.code.Code(mapper), bson.code.Code(reducer), "results", + query={"x": {"$lt": 2}}) + + assert_is_none(async_tracer.active_span) + + spans = self.recorder.queued_spans() + self.assertEqual(len(spans), 2) + + db_span = spans[0] + test_span = spans[1] + + self.assertEqual(test_span.t, db_span.t) + self.assertEqual(db_span.p, test_span.s) + + assert_is_none(db_span.ec) + + self.assertEqual(db_span.n, "mongo") + self.assertEqual(db_span.data["mongo"]["service"], "%s:%s" % (testenv['mongodb_host'], testenv['mongodb_port'])) + self.assertEqual(db_span.data["mongo"]["namespace"], "test.records") + self.assertEqual(db_span.data["mongo"]["command"].lower(), + "mapreduce") # mapreduce command was renamed to mapReduce in pymongo 3.9.0 + + self.assertEqual(db_span.data["mongo"]["filter"], '{"x": {"$lt": 2}}') + assert_is_not_none(db_span.data["mongo"]["json"]) + + payload = json.loads(db_span.data["mongo"]["json"]) + self.assertEqual(payload["map"], {"$code": mapper}, db_span.data["mongo"]["json"]) + self.assertEqual(payload["reduce"], {"$code": reducer}, db_span.data["mongo"]["json"]) + + def test_successful_mutiple_queries(self): + with async_tracer.start_active_span("test"): + self.conn.test.records.bulk_write([pymongo.InsertOne({"type": "string"}), + pymongo.UpdateOne({"type": "string"}, {"$set": {"type": "int"}}), + pymongo.DeleteOne({"type": "string"})]) + + assert_is_none(async_tracer.active_span) + + spans = self.recorder.queued_spans() + self.assertEqual(len(spans), 4) + + test_span = spans.pop() + + seen_span_ids = set() + commands = [] + for span in spans: + self.assertEqual(test_span.t, span.t) + self.assertEqual(span.p, test_span.s) + + # check if all spans got a unique id + assert_false(span.s in seen_span_ids) + + seen_span_ids.add(span.s) + commands.append(span.data["mongo"]["command"]) + + # ensure spans are ordered the same way as commands + assert_list_equal(commands, ["insert", "update", "delete"]) From d0593e40d0a588e21b4e8ef1a7da531c7313642e Mon Sep 17 00:00:00 2001 From: dimitraparaskevopoulou Date: Thu, 10 Jun 2021 16:09:18 +0200 Subject: [PATCH 2/5] initialize async_tracer to avoid import errors --- instana/singletons.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/instana/singletons.py b/instana/singletons.py index 0a973e77..abb5fcae 100644 --- a/instana/singletons.py +++ b/instana/singletons.py @@ -11,6 +11,7 @@ agent = None tracer = None +async_tracer = None profiler = None span_recorder = None @@ -79,11 +80,11 @@ def set_agent(new_agent): if sys.version_info >= (3, 4): try: from opentracing.scope_managers.asyncio import AsyncioScopeManager + async_tracer = InstanaTracer(scope_manager=AsyncioScopeManager(), recorder=span_recorder) except Exception: logger.debug("Error setting up async_tracer:", exc_info=True) - # Mock the tornado tracer until tornado is detected and instrumented first tornado_tracer = tracer @@ -117,6 +118,7 @@ def set_tracer(new_tracer): global tracer tracer = new_tracer + def get_profiler(): """ Retrieve the globally configured profiler From e3401bd7af03840f4444789581054ef9646bcb6a Mon Sep 17 00:00:00 2001 From: dimitraparaskevopoulou Date: Thu, 10 Jun 2021 16:27:40 +0200 Subject: [PATCH 3/5] skip async mongo tests for python2 --- setup.py | 2 +- tests/clients/test_pymongo.py | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 0bb33c1c..123ca06f 100644 --- a/setup.py +++ b/setup.py @@ -64,7 +64,7 @@ def check_setuptools(): 'certifi>=2018.4.16', 'fysom>=2.1.2', 'opentracing>=2.3.0', - 'requests>=2.6.0', # requested to support 2.6.0 + 'requests>=2.8.0', 'six>=1.12.0', 'urllib3<1.26,>=1.21.1'], entry_points={ diff --git a/tests/clients/test_pymongo.py b/tests/clients/test_pymongo.py index 316e667f..492e8f06 100644 --- a/tests/clients/test_pymongo.py +++ b/tests/clients/test_pymongo.py @@ -14,6 +14,8 @@ from instana.singletons import tracer, async_tracer import pymongo +import pytest +import sys import bson logger = logging.getLogger(__name__) @@ -231,7 +233,8 @@ def test_successful_mutiple_queries(self): # ensure spans are ordered the same way as commands assert_list_equal(commands, ["insert", "update", "delete"]) - +@pytest.mark.skipif(sys.version_info[0] < 3 , + reason="testing async tracer for python 3 only") class TestPyMongoAsyncTracer(unittest.TestCase): def setUp(self): self.conn = pymongo.MongoClient(host=testenv['mongodb_host'], port=int(testenv['mongodb_port']), From 7bb93886dc95ccf5a52845a78d46b6316615275a Mon Sep 17 00:00:00 2001 From: dimitraparaskevopoulou Date: Fri, 11 Jun 2021 05:53:27 +0200 Subject: [PATCH 4/5] removing the test replications using async_tracer, it seems to create issues on other test executions --- tests/clients/test_pymongo.py | 217 +--------------------------------- 1 file changed, 1 insertion(+), 216 deletions(-) diff --git a/tests/clients/test_pymongo.py b/tests/clients/test_pymongo.py index 492e8f06..f205c320 100644 --- a/tests/clients/test_pymongo.py +++ b/tests/clients/test_pymongo.py @@ -11,11 +11,9 @@ assert_false, assert_true, assert_list_equal) from ..helpers import testenv -from instana.singletons import tracer, async_tracer +from instana.singletons import tracer import pymongo -import pytest -import sys import bson logger = logging.getLogger(__name__) @@ -233,216 +231,3 @@ def test_successful_mutiple_queries(self): # ensure spans are ordered the same way as commands assert_list_equal(commands, ["insert", "update", "delete"]) -@pytest.mark.skipif(sys.version_info[0] < 3 , - reason="testing async tracer for python 3 only") -class TestPyMongoAsyncTracer(unittest.TestCase): - def setUp(self): - self.conn = pymongo.MongoClient(host=testenv['mongodb_host'], port=int(testenv['mongodb_port']), - username=testenv['mongodb_user'], password=testenv['mongodb_pw']) - self.conn.test.records.delete_many(filter={}) - - self.recorder = async_tracer.recorder - self.recorder.clear_spans() - - def tearDown(self): - return None - - def test_successful_find_query(self): - with async_tracer.start_active_span("test"): - self.conn.test.records.find_one({"type": "string"}) - - assert_is_none(async_tracer.active_span) - - spans = self.recorder.queued_spans() - self.assertEqual(len(spans), 2) - - db_span = spans[0] - test_span = spans[1] - - self.assertEqual(test_span.t, db_span.t) - self.assertEqual(db_span.p, test_span.s) - - assert_is_none(db_span.ec) - - self.assertEqual(db_span.n, "mongo") - self.assertEqual(db_span.data["mongo"]["service"], "%s:%s" % (testenv['mongodb_host'], testenv['mongodb_port'])) - self.assertEqual(db_span.data["mongo"]["namespace"], "test.records") - self.assertEqual(db_span.data["mongo"]["command"], "find") - - self.assertEqual(db_span.data["mongo"]["filter"], '{"type": "string"}') - assert_is_none(db_span.data["mongo"]["json"]) - - def test_successful_insert_query(self): - with async_tracer.start_active_span("test"): - self.conn.test.records.insert_one({"type": "string"}) - - assert_is_none(async_tracer.active_span) - - spans = self.recorder.queued_spans() - self.assertEqual(len(spans), 2) - - db_span = spans[0] - test_span = spans[1] - - self.assertEqual(test_span.t, db_span.t) - self.assertEqual(db_span.p, test_span.s) - - assert_is_none(db_span.ec) - - self.assertEqual(db_span.n, "mongo") - self.assertEqual(db_span.data["mongo"]["service"], "%s:%s" % (testenv['mongodb_host'], testenv['mongodb_port'])) - self.assertEqual(db_span.data["mongo"]["namespace"], "test.records") - self.assertEqual(db_span.data["mongo"]["command"], "insert") - - assert_is_none(db_span.data["mongo"]["filter"]) - - def test_successful_update_query(self): - with async_tracer.start_active_span("test"): - self.conn.test.records.update_one({"type": "string"}, {"$set": {"type": "int"}}) - - assert_is_none(async_tracer.active_span) - - spans = self.recorder.queued_spans() - self.assertEqual(len(spans), 2) - - db_span = spans[0] - test_span = spans[1] - - self.assertEqual(test_span.t, db_span.t) - self.assertEqual(db_span.p, test_span.s) - - assert_is_none(db_span.ec) - - self.assertEqual(db_span.n, "mongo") - self.assertEqual(db_span.data["mongo"]["service"], "%s:%s" % (testenv['mongodb_host'], testenv['mongodb_port'])) - self.assertEqual(db_span.data["mongo"]["namespace"], "test.records") - self.assertEqual(db_span.data["mongo"]["command"], "update") - - assert_is_none(db_span.data["mongo"]["filter"]) - assert_is_not_none(db_span.data["mongo"]["json"]) - - payload = json.loads(db_span.data["mongo"]["json"]) - assert_true({ - "q": {"type": "string"}, - "u": {"$set": {"type": "int"}}, - "multi": False, - "upsert": False - } in payload, db_span.data["mongo"]["json"]) - - def test_successful_delete_query(self): - with async_tracer.start_active_span("test"): - self.conn.test.records.delete_one(filter={"type": "string"}) - - assert_is_none(async_tracer.active_span) - - spans = self.recorder.queued_spans() - self.assertEqual(len(spans), 2) - - db_span = spans[0] - test_span = spans[1] - - self.assertEqual(test_span.t, db_span.t) - self.assertEqual(db_span.p, test_span.s) - - assert_is_none(db_span.ec) - - self.assertEqual(db_span.n, "mongo") - self.assertEqual(db_span.data["mongo"]["service"], "%s:%s" % (testenv['mongodb_host'], testenv['mongodb_port'])) - self.assertEqual(db_span.data["mongo"]["namespace"], "test.records") - self.assertEqual(db_span.data["mongo"]["command"], "delete") - - assert_is_none(db_span.data["mongo"]["filter"]) - assert_is_not_none(db_span.data["mongo"]["json"]) - - payload = json.loads(db_span.data["mongo"]["json"]) - assert_true({"q": {"type": "string"}, "limit": 1} in payload, db_span.data["mongo"]["json"]) - - def test_successful_aggregate_query(self): - with async_tracer.start_active_span("test"): - self.conn.test.records.count_documents({"type": "string"}) - - assert_is_none(async_tracer.active_span) - - spans = self.recorder.queued_spans() - self.assertEqual(len(spans), 2) - - db_span = spans[0] - test_span = spans[1] - - self.assertEqual(test_span.t, db_span.t) - self.assertEqual(db_span.p, test_span.s) - - assert_is_none(db_span.ec) - - self.assertEqual(db_span.n, "mongo") - self.assertEqual(db_span.data["mongo"]["service"], "%s:%s" % (testenv['mongodb_host'], testenv['mongodb_port'])) - self.assertEqual(db_span.data["mongo"]["namespace"], "test.records") - self.assertEqual(db_span.data["mongo"]["command"], "aggregate") - - assert_is_none(db_span.data["mongo"]["filter"]) - assert_is_not_none(db_span.data["mongo"]["json"]) - - payload = json.loads(db_span.data["mongo"]["json"]) - assert_true({"$match": {"type": "string"}} in payload, db_span.data["mongo"]["json"]) - - def test_successful_map_reduce_query(self): - mapper = "function () { this.tags.forEach(function(z) { emit(z, 1); }); }" - reducer = "function (key, values) { return len(values); }" - - with async_tracer.start_active_span("test"): - self.conn.test.records.map_reduce(bson.code.Code(mapper), bson.code.Code(reducer), "results", - query={"x": {"$lt": 2}}) - - assert_is_none(async_tracer.active_span) - - spans = self.recorder.queued_spans() - self.assertEqual(len(spans), 2) - - db_span = spans[0] - test_span = spans[1] - - self.assertEqual(test_span.t, db_span.t) - self.assertEqual(db_span.p, test_span.s) - - assert_is_none(db_span.ec) - - self.assertEqual(db_span.n, "mongo") - self.assertEqual(db_span.data["mongo"]["service"], "%s:%s" % (testenv['mongodb_host'], testenv['mongodb_port'])) - self.assertEqual(db_span.data["mongo"]["namespace"], "test.records") - self.assertEqual(db_span.data["mongo"]["command"].lower(), - "mapreduce") # mapreduce command was renamed to mapReduce in pymongo 3.9.0 - - self.assertEqual(db_span.data["mongo"]["filter"], '{"x": {"$lt": 2}}') - assert_is_not_none(db_span.data["mongo"]["json"]) - - payload = json.loads(db_span.data["mongo"]["json"]) - self.assertEqual(payload["map"], {"$code": mapper}, db_span.data["mongo"]["json"]) - self.assertEqual(payload["reduce"], {"$code": reducer}, db_span.data["mongo"]["json"]) - - def test_successful_mutiple_queries(self): - with async_tracer.start_active_span("test"): - self.conn.test.records.bulk_write([pymongo.InsertOne({"type": "string"}), - pymongo.UpdateOne({"type": "string"}, {"$set": {"type": "int"}}), - pymongo.DeleteOne({"type": "string"})]) - - assert_is_none(async_tracer.active_span) - - spans = self.recorder.queued_spans() - self.assertEqual(len(spans), 4) - - test_span = spans.pop() - - seen_span_ids = set() - commands = [] - for span in spans: - self.assertEqual(test_span.t, span.t) - self.assertEqual(span.p, test_span.s) - - # check if all spans got a unique id - assert_false(span.s in seen_span_ids) - - seen_span_ids.add(span.s) - commands.append(span.data["mongo"]["command"]) - - # ensure spans are ordered the same way as commands - assert_list_equal(commands, ["insert", "update", "delete"]) From 47b603700b7905858f0e46a35d917972fde0e406 Mon Sep 17 00:00:00 2001 From: dimitraparaskevopoulou Date: Mon, 14 Jun 2021 13:33:42 +0200 Subject: [PATCH 5/5] refactor the tracer discovery functionality into a tracer utility method --- instana/instrumentation/pymongo.py | 18 +++++++++--------- instana/util/traceutils.py | 15 ++++++++++++++- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/instana/instrumentation/pymongo.py b/instana/instrumentation/pymongo.py index bac19364..5cf892ae 100644 --- a/instana/instrumentation/pymongo.py +++ b/instana/instrumentation/pymongo.py @@ -4,26 +4,25 @@ from __future__ import absolute_import from ..log import logger -from ..singletons import tracer, async_tracer +from ..util.traceutils import get_active_tracer try: import pymongo from pymongo import monitoring from bson import json_util + class MongoCommandTracer(monitoring.CommandListener): def __init__(self): self.__active_commands = {} def started(self, event): - parent_span = tracer.active_span - active_tracer = tracer + active_tracer = get_active_tracer() # return early if we're not tracing - if parent_span is None: - parent_span = async_tracer.active_span - if parent_span is None: - return - active_tracer = async_tracer + if active_tracer is None: + return + + parent_span = active_tracer.active_span with active_tracer.start_active_span("mongo", child_of=parent_span) as scope: self._collect_connection_tags(scope.span, event) @@ -82,7 +81,7 @@ def _collect_command_tags(self, span, event): cmd_doc = None if cmd in cmd_doc_locations: cmd_doc = event.command.get(cmd_doc_locations[cmd]) - elif cmd.lower() == "mapreduce": # mapreduce command was renamed to mapReduce in pymongo 3.9.0 + elif cmd.lower() == "mapreduce": # mapreduce command was renamed to mapReduce in pymongo 3.9.0 # mapreduce command consists of two mandatory parts: map and reduce cmd_doc = { "map": event.command.get("map"), @@ -92,6 +91,7 @@ def _collect_command_tags(self, span, event): if cmd_doc is not None: span.set_tag("json", json_util.dumps(cmd_doc)) + monitoring.register(MongoCommandTracer()) logger.debug("Instrumenting pymongo") diff --git a/instana/util/traceutils.py b/instana/util/traceutils.py index e93619fe..8f095bed 100644 --- a/instana/util/traceutils.py +++ b/instana/util/traceutils.py @@ -1,7 +1,7 @@ # (c) Copyright IBM Corp. 2021 # (c) Copyright Instana Inc. 2021 -from ..singletons import agent +from ..singletons import agent, tracer, async_tracer from ..log import logger @@ -14,3 +14,16 @@ def extract_custom_headers(tracing_scope, headers): tracing_scope.span.set_tag("http.header.%s" % custom_header, value) except Exception as e: logger.debug("extract_custom_headers: ", exc_info=True) + + +def get_active_tracer(): + try: + if tracer.active_span: + return tracer + elif async_tracer.active_span: + return async_tracer + else: + return None + except Exception as e: + logger.debug("error while getting active tracer: ", exc_info=True) + return None