diff --git a/instrumentation/opentelemetry-instrumentation-aiopg/src/opentelemetry/instrumentation/aiopg/__init__.py b/instrumentation/opentelemetry-instrumentation-aiopg/src/opentelemetry/instrumentation/aiopg/__init__.py index d2cc90902eb..176fc82b40c 100644 --- a/instrumentation/opentelemetry-instrumentation-aiopg/src/opentelemetry/instrumentation/aiopg/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-aiopg/src/opentelemetry/instrumentation/aiopg/__init__.py @@ -63,7 +63,7 @@ class AiopgInstrumentor(BaseInstrumentor): def _instrument(self, **kwargs): """Integrate with PostgreSQL aiopg library. - aiopg: https://github.com/aio-libs/aiopg + aiopg: https://github.com/aio-libs/aiopg """ tracer_provider = kwargs.get("tracer_provider") diff --git a/instrumentation/opentelemetry-instrumentation-aiopg/src/opentelemetry/instrumentation/aiopg/aiopg_integration.py b/instrumentation/opentelemetry-instrumentation-aiopg/src/opentelemetry/instrumentation/aiopg/aiopg_integration.py index b6992120f3c..1455f23e629 100644 --- a/instrumentation/opentelemetry-instrumentation-aiopg/src/opentelemetry/instrumentation/aiopg/aiopg_integration.py +++ b/instrumentation/opentelemetry-instrumentation-aiopg/src/opentelemetry/instrumentation/aiopg/aiopg_integration.py @@ -37,8 +37,7 @@ async def wrapped_connection( args: typing.Tuple[typing.Any, typing.Any], kwargs: typing.Dict[typing.Any, typing.Any], ): - """Add object proxy to connection object. - """ + """Add object proxy to connection object.""" connection = await connect_method(*args, **kwargs) # pylint: disable=protected-access self.get_connection_attributes(connection._conn) @@ -109,10 +108,14 @@ async def traced_execution( self._populate_span(span, *args) try: result = await query_method(*args, **kwargs) - span.set_status(Status(StatusCanonicalCode.OK)) + if span.is_recording(): + span.set_status(Status(StatusCanonicalCode.OK)) return result except Exception as ex: # pylint: disable=broad-except - span.set_status(Status(StatusCanonicalCode.UNKNOWN, str(ex))) + if span.is_recording(): + span.set_status( + Status(StatusCanonicalCode.UNKNOWN, str(ex)) + ) raise ex diff --git a/instrumentation/opentelemetry-instrumentation-aiopg/src/opentelemetry/instrumentation/aiopg/wrappers.py b/instrumentation/opentelemetry-instrumentation-aiopg/src/opentelemetry/instrumentation/aiopg/wrappers.py index 473c5039c32..8a3b6023bd5 100644 --- a/instrumentation/opentelemetry-instrumentation-aiopg/src/opentelemetry/instrumentation/aiopg/wrappers.py +++ b/instrumentation/opentelemetry-instrumentation-aiopg/src/opentelemetry/instrumentation/aiopg/wrappers.py @@ -56,16 +56,16 @@ def trace_integration( tracer_provider: typing.Optional[TracerProvider] = None, ): """Integrate with aiopg library. - based on dbapi integration, where replaced sync wrap methods to async - - Args: - database_component: Database driver name or - database name "postgreSQL". - database_type: The Database type. For any SQL database, "sql". - connection_attributes: Attribute names for database, port, host and - user in Connection object. - tracer_provider: The :class:`opentelemetry.trace.TracerProvider` to - use. If ommited the current configured one is used. + based on dbapi integration, where replaced sync wrap methods to async + + Args: + database_component: Database driver name or + database name "postgreSQL". + database_type: The Database type. For any SQL database, "sql". + connection_attributes: Attribute names for database, port, host and + user in Connection object. + tracer_provider: The :class:`opentelemetry.trace.TracerProvider` to + use. If ommited the current configured one is used. """ wrap_connect( @@ -87,18 +87,18 @@ def wrap_connect( tracer_provider: typing.Optional[TracerProvider] = None, ): """Integrate with aiopg library. - https://github.com/aio-libs/aiopg - - Args: - name: Name of opentelemetry extension for aiopg. - database_component: Database driver name - or database name "postgreSQL". - database_type: The Database type. For any SQL database, "sql". - connection_attributes: Attribute names for database, port, host and - user in Connection object. - version: Version of opentelemetry extension for aiopg. - tracer_provider: The :class:`opentelemetry.trace.TracerProvider` to - use. If ommited the current configured one is used. + https://github.com/aio-libs/aiopg + + Args: + name: Name of opentelemetry extension for aiopg. + database_component: Database driver name + or database name "postgreSQL". + database_type: The Database type. For any SQL database, "sql". + connection_attributes: Attribute names for database, port, host and + user in Connection object. + version: Version of opentelemetry extension for aiopg. + tracer_provider: The :class:`opentelemetry.trace.TracerProvider` to + use. If ommited the current configured one is used. """ # pylint: disable=unused-argument @@ -125,8 +125,8 @@ async def wrap_connect_( def unwrap_connect(): - """"Disable integration with aiopg library. - https://github.com/aio-libs/aiopg + """Disable integration with aiopg library. + https://github.com/aio-libs/aiopg """ unwrap(aiopg, "connect") @@ -217,7 +217,7 @@ async def wrap_create_pool_( def unwrap_create_pool(): - """"Disable integration with aiopg library. - https://github.com/aio-libs/aiopg + """Disable integration with aiopg library. + https://github.com/aio-libs/aiopg """ unwrap(aiopg, "create_pool") diff --git a/instrumentation/opentelemetry-instrumentation-aiopg/tests/test_aiopg_integration.py b/instrumentation/opentelemetry-instrumentation-aiopg/tests/test_aiopg_integration.py index f7daf7ccc0f..135f9ee9a78 100644 --- a/instrumentation/opentelemetry-instrumentation-aiopg/tests/test_aiopg_integration.py +++ b/instrumentation/opentelemetry-instrumentation-aiopg/tests/test_aiopg_integration.py @@ -230,6 +230,40 @@ def test_span_succeeded(self): trace_api.status.StatusCanonicalCode.OK, ) + def test_span_not_recording(self): + connection_props = { + "database": "testdatabase", + "server_host": "testhost", + "server_port": 123, + "user": "testuser", + } + connection_attributes = { + "database": "database", + "port": "server_port", + "host": "server_host", + "user": "user", + } + mock_tracer = mock.Mock() + mock_span = mock.Mock() + mock_span.is_recording.return_value = False + mock_tracer.start_span.return_value = mock_span + mock_tracer.use_span.return_value.__enter__ = mock_span + mock_tracer.use_span.return_value.__exit__ = True + db_integration = AiopgIntegration( + mock_tracer, "testcomponent", "testtype", connection_attributes + ) + mock_connection = async_call( + db_integration.wrapped_connection( + mock_connect, {}, connection_props + ) + ) + cursor = async_call(mock_connection.cursor()) + async_call(cursor.execute("Test query", ("param1Value", False))) + 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_span_failed(self): db_integration = AiopgIntegration(self.tracer, "testcomponent") mock_connection = async_call( diff --git a/instrumentation/opentelemetry-instrumentation-asyncpg/src/opentelemetry/instrumentation/asyncpg/__init__.py b/instrumentation/opentelemetry-instrumentation-asyncpg/src/opentelemetry/instrumentation/asyncpg/__init__.py index 189809809d9..6af816b39df 100644 --- a/instrumentation/opentelemetry-instrumentation-asyncpg/src/opentelemetry/instrumentation/asyncpg/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-asyncpg/src/opentelemetry/instrumentation/asyncpg/__init__.py @@ -112,9 +112,6 @@ def _uninstrument(self, **__): unwrap(asyncpg.Connection, method) async def _do_execute(self, func, instance, args, kwargs): - span_attributes = _hydrate_span_from_args( - instance, args[0], args[1:] if self.capture_parameters else None, - ) tracer = getattr(asyncpg, _APPLIED) exception = None @@ -122,9 +119,14 @@ async def _do_execute(self, func, instance, args, kwargs): with tracer.start_as_current_span( "postgresql", kind=SpanKind.CLIENT ) as span: - - for attribute, value in span_attributes.items(): - span.set_attribute(attribute, value) + if span.is_recording(): + span_attributes = _hydrate_span_from_args( + instance, + args[0], + args[1:] if self.capture_parameters else None, + ) + for attribute, value in span_attributes.items(): + span.set_attribute(attribute, value) try: result = await func(*args, **kwargs) @@ -132,11 +134,12 @@ async def _do_execute(self, func, instance, args, kwargs): exception = exc raise finally: - if exception is not None: - span.set_status( - Status(_exception_to_canonical_code(exception)) - ) - else: - span.set_status(Status(StatusCanonicalCode.OK)) + if span.is_recording(): + if exception is not None: + span.set_status( + Status(_exception_to_canonical_code(exception)) + ) + else: + span.set_status(Status(StatusCanonicalCode.OK)) return result diff --git a/instrumentation/opentelemetry-instrumentation-boto/tests/test_boto_instrumentation.py b/instrumentation/opentelemetry-instrumentation-boto/tests/test_boto_instrumentation.py index 1a8cc2b387f..cb45514c799 100644 --- a/instrumentation/opentelemetry-instrumentation-boto/tests/test_boto_instrumentation.py +++ b/instrumentation/opentelemetry-instrumentation-boto/tests/test_boto_instrumentation.py @@ -88,7 +88,7 @@ def test_not_recording(self): mock_span.is_recording.return_value = False mock_tracer.start_span.return_value = mock_span mock_tracer.use_span.return_value.__enter__ = mock_span - mock_tracer.use_span.return_value.__exit__ = mock_span + mock_tracer.use_span.return_value.__exit__ = True with patch("opentelemetry.trace.get_tracer") as tracer: tracer.return_value = mock_tracer ec2 = boto.ec2.connect_to_region("us-west-2") diff --git a/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_instrumentation.py b/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_instrumentation.py index 9bf691f6570..fba0182eec4 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_instrumentation.py +++ b/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_instrumentation.py @@ -84,7 +84,7 @@ def test_not_recording(self): mock_span.is_recording.return_value = False mock_tracer.start_span.return_value = mock_span mock_tracer.use_span.return_value.__enter__ = mock_span - mock_tracer.use_span.return_value.__exit__ = mock_span + mock_tracer.use_span.return_value.__exit__ = True with patch("opentelemetry.trace.get_tracer") as tracer: tracer.return_value = mock_tracer ec2 = self.session.create_client("ec2", region_name="us-west-2") diff --git a/instrumentation/opentelemetry-instrumentation-dbapi/src/opentelemetry/instrumentation/dbapi/__init__.py b/instrumentation/opentelemetry-instrumentation-dbapi/src/opentelemetry/instrumentation/dbapi/__init__.py index 551f71555ad..0dcdd5ba606 100644 --- a/instrumentation/opentelemetry-instrumentation-dbapi/src/opentelemetry/instrumentation/dbapi/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-dbapi/src/opentelemetry/instrumentation/dbapi/__init__.py @@ -311,6 +311,8 @@ def __init__(self, db_api_integration: DatabaseApiIntegration): def _populate_span( self, span: trace_api.Span, *args: typing.Tuple[typing.Any, typing.Any] ): + if not span.is_recording(): + return statement = args[0] if args else "" span.set_attribute( "component", self._db_api_integration.database_component @@ -341,10 +343,14 @@ def traced_execution( self._populate_span(span, *args) try: result = query_method(*args, **kwargs) - span.set_status(Status(StatusCanonicalCode.OK)) + if span.is_recording(): + span.set_status(Status(StatusCanonicalCode.OK)) return result except Exception as ex: # pylint: disable=broad-except - span.set_status(Status(StatusCanonicalCode.UNKNOWN, str(ex))) + if span.is_recording(): + span.set_status( + Status(StatusCanonicalCode.UNKNOWN, str(ex)) + ) raise ex diff --git a/instrumentation/opentelemetry-instrumentation-dbapi/tests/test_dbapi_integration.py b/instrumentation/opentelemetry-instrumentation-dbapi/tests/test_dbapi_integration.py index 29915703339..e342e15aa34 100644 --- a/instrumentation/opentelemetry-instrumentation-dbapi/tests/test_dbapi_integration.py +++ b/instrumentation/opentelemetry-instrumentation-dbapi/tests/test_dbapi_integration.py @@ -69,6 +69,38 @@ def test_span_succeeded(self): trace_api.status.StatusCanonicalCode.OK, ) + def test_span_not_recording(self): + connection_props = { + "database": "testdatabase", + "server_host": "testhost", + "server_port": 123, + "user": "testuser", + } + connection_attributes = { + "database": "database", + "port": "server_port", + "host": "server_host", + "user": "user", + } + mock_tracer = mock.Mock() + mock_span = mock.Mock() + mock_span.is_recording.return_value = False + mock_tracer.start_span.return_value = mock_span + mock_tracer.use_span.return_value.__enter__ = mock_span + mock_tracer.use_span.return_value.__exit__ = True + db_integration = dbapi.DatabaseApiIntegration( + mock_tracer, "testcomponent", "testtype", connection_attributes + ) + mock_connection = db_integration.wrapped_connection( + mock_connect, {}, connection_props + ) + cursor = mock_connection.cursor() + cursor.execute("Test query", ("param1Value", False)) + 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_span_failed(self): db_integration = dbapi.DatabaseApiIntegration( self.tracer, "testcomponent" diff --git a/instrumentation/opentelemetry-instrumentation-django/tests/test_middleware.py b/instrumentation/opentelemetry-instrumentation-django/tests/test_middleware.py index 7e9ab72b523..4db6c485dec 100644 --- a/instrumentation/opentelemetry-instrumentation-django/tests/test_middleware.py +++ b/instrumentation/opentelemetry-instrumentation-django/tests/test_middleware.py @@ -95,7 +95,7 @@ def test_not_recording(self): mock_span.is_recording.return_value = False mock_tracer.start_span.return_value = mock_span mock_tracer.use_span.return_value.__enter__ = mock_span - mock_tracer.use_span.return_value.__exit__ = mock_span + mock_tracer.use_span.return_value.__exit__ = True with patch("opentelemetry.trace.get_tracer") as tracer: tracer.return_value = mock_tracer Client().get("/traced/") diff --git a/instrumentation/opentelemetry-instrumentation-psycopg2/tests/test_psycopg2_integration.py b/instrumentation/opentelemetry-instrumentation-psycopg2/tests/test_psycopg2_integration.py index 629b2f62b65..cb127c7a5ef 100644 --- a/instrumentation/opentelemetry-instrumentation-psycopg2/tests/test_psycopg2_integration.py +++ b/instrumentation/opentelemetry-instrumentation-psycopg2/tests/test_psycopg2_integration.py @@ -60,6 +60,29 @@ def test_instrumentor(self, mock_connect): spans_list = self.memory_exporter.get_finished_spans() self.assertEqual(len(spans_list), 1) + @mock.patch("psycopg2.connect") + # pylint: disable=unused-argument + def test_not_recording(self, mock_connect): + mock_tracer = mock.Mock() + mock_span = mock.Mock() + mock_span.is_recording.return_value = False + mock_tracer.start_span.return_value = mock_span + mock_tracer.use_span.return_value.__enter__ = mock_span + mock_tracer.use_span.return_value.__exit__ = True + Psycopg2Instrumentor().instrument() + with mock.patch("opentelemetry.trace.get_tracer") as tracer: + tracer.return_value = mock_tracer + cnx = psycopg2.connect(database="test") + cursor = cnx.cursor() + query = "SELECT * FROM test" + cursor.execute(query) + 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) + + Psycopg2Instrumentor().uninstrument() + @mock.patch("psycopg2.connect") # pylint: disable=unused-argument def test_custom_tracer_provider(self, mock_connect): diff --git a/instrumentation/opentelemetry-instrumentation-pymemcache/src/opentelemetry/instrumentation/pymemcache/__init__.py b/instrumentation/opentelemetry-instrumentation-pymemcache/src/opentelemetry/instrumentation/pymemcache/__init__.py index 332e92ccdde..46b188a3dfa 100644 --- a/instrumentation/opentelemetry-instrumentation-pymemcache/src/opentelemetry/instrumentation/pymemcache/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-pymemcache/src/opentelemetry/instrumentation/pymemcache/__init__.py @@ -90,13 +90,14 @@ def _set_connection_attributes(span, instance): + if not span.is_recording(): + return for key, value in _get_address_attributes(instance).items(): span.set_attribute(key, value) def _with_tracer_wrapper(func): - """Helper for providing tracer for wrapper functions. - """ + """Helper for providing tracer for wrapper functions.""" def _with_tracer(tracer, cmd): def wrapper(wrapped, instance, args, kwargs): diff --git a/instrumentation/opentelemetry-instrumentation-pymemcache/tests/test_pymemcache.py b/instrumentation/opentelemetry-instrumentation-pymemcache/tests/test_pymemcache.py index 794da9972be..b38bedf3fd8 100644 --- a/instrumentation/opentelemetry-instrumentation-pymemcache/tests/test_pymemcache.py +++ b/instrumentation/opentelemetry-instrumentation-pymemcache/tests/test_pymemcache.py @@ -11,6 +11,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +from unittest import mock import pymemcache from pymemcache.exceptions import ( @@ -84,6 +85,23 @@ def test_set_success(self): self.check_spans(spans, 1, ["set key"]) + def test_set_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 + mock_tracer.use_span.return_value.__enter__ = mock_span + mock_tracer.use_span.return_value.__exit__ = True + with mock.patch("opentelemetry.trace.get_tracer") as tracer: + tracer.return_value = mock_tracer + client = self.make_client([b"STORED\r\n"]) + result = client.set(b"key", b"value", noreply=False) + self.assertTrue(result) + 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_get_many_none_found(self): client = self.make_client([b"END\r\n"]) result = client.get_many([b"key1", b"key2"]) diff --git a/instrumentation/opentelemetry-instrumentation-pymongo/src/opentelemetry/instrumentation/pymongo/__init__.py b/instrumentation/opentelemetry-instrumentation-pymongo/src/opentelemetry/instrumentation/pymongo/__init__.py index 4cfff745d17..bb20fd5442c 100644 --- a/instrumentation/opentelemetry-instrumentation-pymongo/src/opentelemetry/instrumentation/pymongo/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-pymongo/src/opentelemetry/instrumentation/pymongo/__init__.py @@ -71,28 +71,32 @@ def started(self, event: monitoring.CommandStartedEvent): try: span = self._tracer.start_span(name, kind=SpanKind.CLIENT) - span.set_attribute("component", DATABASE_TYPE) - span.set_attribute("db.type", DATABASE_TYPE) - span.set_attribute("db.instance", event.database_name) - span.set_attribute("db.statement", statement) - if event.connection_id is not None: - span.set_attribute("net.peer.name", event.connection_id[0]) - span.set_attribute("net.peer.port", event.connection_id[1]) - - # pymongo specific, not specified by spec - span.set_attribute("db.mongo.operation_id", event.operation_id) - span.set_attribute("db.mongo.request_id", event.request_id) - - for attr in COMMAND_ATTRIBUTES: - _attr = event.command.get(attr) - if _attr is not None: - span.set_attribute("db.mongo." + attr, str(_attr)) + if span.is_recording(): + span.set_attribute("component", DATABASE_TYPE) + span.set_attribute("db.type", DATABASE_TYPE) + span.set_attribute("db.instance", event.database_name) + span.set_attribute("db.statement", statement) + if event.connection_id is not None: + span.set_attribute("net.peer.name", event.connection_id[0]) + span.set_attribute("net.peer.port", event.connection_id[1]) + + # pymongo specific, not specified by spec + span.set_attribute("db.mongo.operation_id", event.operation_id) + span.set_attribute("db.mongo.request_id", event.request_id) + + for attr in COMMAND_ATTRIBUTES: + _attr = event.command.get(attr) + if _attr is not None: + span.set_attribute("db.mongo." + attr, str(_attr)) # Add Span to dictionary self._span_dict[_get_span_dict_key(event)] = span except Exception as ex: # noqa pylint: disable=broad-except if span is not None: - span.set_status(Status(StatusCanonicalCode.INTERNAL, str(ex))) + if span.is_recording(): + span.set_status( + Status(StatusCanonicalCode.INTERNAL, str(ex)) + ) span.end() self._pop_span(event) @@ -103,8 +107,11 @@ def succeeded(self, event: monitoring.CommandSucceededEvent): span = self._pop_span(event) if span is None: return - span.set_attribute("db.mongo.duration_micros", event.duration_micros) - span.set_status(Status(StatusCanonicalCode.OK, event.reply)) + if span.is_recording(): + span.set_attribute( + "db.mongo.duration_micros", event.duration_micros + ) + span.set_status(Status(StatusCanonicalCode.OK, event.reply)) span.end() def failed(self, event: monitoring.CommandFailedEvent): @@ -114,8 +121,11 @@ def failed(self, event: monitoring.CommandFailedEvent): span = self._pop_span(event) if span is None: return - span.set_attribute("db.mongo.duration_micros", event.duration_micros) - span.set_status(Status(StatusCanonicalCode.UNKNOWN, event.failure)) + if span.is_recording(): + span.set_attribute( + "db.mongo.duration_micros", event.duration_micros + ) + span.set_status(Status(StatusCanonicalCode.UNKNOWN, event.failure)) span.end() def _pop_span(self, event): diff --git a/instrumentation/opentelemetry-instrumentation-pymongo/tests/test_pymongo.py b/instrumentation/opentelemetry-instrumentation-pymongo/tests/test_pymongo.py index a84841b28b2..d5f67cafe8c 100644 --- a/instrumentation/opentelemetry-instrumentation-pymongo/tests/test_pymongo.py +++ b/instrumentation/opentelemetry-instrumentation-pymongo/tests/test_pymongo.py @@ -91,6 +91,22 @@ def test_succeeded(self): self.assertEqual(span.status.description, "reply") self.assertIsNotNone(span.end_time) + 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 + mock_tracer.use_span.return_value.__enter__ = mock_span + mock_tracer.use_span.return_value.__exit__ = True + mock_event = MockEvent({}) + command_tracer = CommandTracer(mock_tracer) + command_tracer.started(event=mock_event) + command_tracer.succeeded(event=mock_event) + 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_failed(self): mock_event = MockEvent({}) command_tracer = CommandTracer(self.tracer) diff --git a/instrumentation/opentelemetry-instrumentation-pyramid/tests/test_programmatic.py b/instrumentation/opentelemetry-instrumentation-pyramid/tests/test_programmatic.py index 38ba71cb55d..77427b0db7a 100644 --- a/instrumentation/opentelemetry-instrumentation-pyramid/tests/test_programmatic.py +++ b/instrumentation/opentelemetry-instrumentation-pyramid/tests/test_programmatic.py @@ -93,7 +93,7 @@ def test_not_recording(self): mock_span.is_recording.return_value = False mock_tracer.start_span.return_value = mock_span mock_tracer.use_span.return_value.__enter__ = mock_span - mock_tracer.use_span.return_value.__exit__ = mock_span + mock_tracer.use_span.return_value.__exit__ = True with patch("opentelemetry.trace.get_tracer"): self.client.get("/hello/123") span_list = self.memory_exporter.get_finished_spans() diff --git a/instrumentation/opentelemetry-instrumentation-redis/src/opentelemetry/instrumentation/redis/__init__.py b/instrumentation/opentelemetry-instrumentation-redis/src/opentelemetry/instrumentation/redis/__init__.py index e2ab78b5356..e1c5db1e940 100644 --- a/instrumentation/opentelemetry-instrumentation-redis/src/opentelemetry/instrumentation/redis/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-redis/src/opentelemetry/instrumentation/redis/__init__.py @@ -63,6 +63,8 @@ def _set_connection_attributes(span, conn): + if not span.is_recording(): + return for key, value in _extract_conn_attributes( conn.connection_pool.connection_kwargs ).items(): @@ -75,10 +77,11 @@ def _traced_execute_command(func, instance, args, kwargs): with tracer.start_as_current_span( _CMD, kind=trace.SpanKind.CLIENT ) as span: - span.set_attribute("service", tracer.instrumentation_info.name) - span.set_attribute(_RAWCMD, query) - _set_connection_attributes(span, instance) - span.set_attribute("redis.args_length", len(args)) + if span.is_recording(): + span.set_attribute("service", tracer.instrumentation_info.name) + span.set_attribute(_RAWCMD, query) + _set_connection_attributes(span, instance) + span.set_attribute("redis.args_length", len(args)) return func(*args, **kwargs) @@ -91,12 +94,13 @@ def _traced_execute_pipeline(func, instance, args, kwargs): with tracer.start_as_current_span( _CMD, kind=trace.SpanKind.CLIENT ) as span: - span.set_attribute("service", tracer.instrumentation_info.name) - span.set_attribute(_RAWCMD, resource) - _set_connection_attributes(span, instance) - span.set_attribute( - "redis.pipeline_length", len(instance.command_stack) - ) + if span.is_recording(): + span.set_attribute("service", tracer.instrumentation_info.name) + span.set_attribute(_RAWCMD, resource) + _set_connection_attributes(span, instance) + span.set_attribute( + "redis.pipeline_length", len(instance.command_stack) + ) return func(*args, **kwargs) diff --git a/instrumentation/opentelemetry-instrumentation-redis/tests/test_redis.py b/instrumentation/opentelemetry-instrumentation-redis/tests/test_redis.py index c306dca3637..3e07ac725e2 100644 --- a/instrumentation/opentelemetry-instrumentation-redis/tests/test_redis.py +++ b/instrumentation/opentelemetry-instrumentation-redis/tests/test_redis.py @@ -34,6 +34,25 @@ def test_span_properties(self): self.assertEqual(span.name, "redis.command") self.assertEqual(span.kind, SpanKind.CLIENT) + def test_not_recording(self): + redis_client = redis.Redis() + RedisInstrumentor().instrument(tracer_provider=self.tracer_provider) + + mock_tracer = mock.Mock() + mock_span = mock.Mock() + mock_span.is_recording.return_value = False + mock_tracer.start_span.return_value = mock_span + mock_tracer.use_span.return_value.__enter__ = mock_span + mock_tracer.use_span.return_value.__exit__ = True + with mock.patch("opentelemetry.trace.get_tracer") as tracer: + with mock.patch.object(redis_client, "connection"): + tracer.return_value = mock_tracer + redis_client.get("key") + 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_instrument_uninstrument(self): redis_client = redis.Redis() RedisInstrumentor().instrument(tracer_provider=self.tracer_provider) diff --git a/instrumentation/opentelemetry-instrumentation-sqlalchemy/src/opentelemetry/instrumentation/sqlalchemy/engine.py b/instrumentation/opentelemetry-instrumentation-sqlalchemy/src/opentelemetry/instrumentation/sqlalchemy/engine.py index df80c4841c3..83a5b82b235 100644 --- a/instrumentation/opentelemetry-instrumentation-sqlalchemy/src/opentelemetry/instrumentation/sqlalchemy/engine.py +++ b/instrumentation/opentelemetry-instrumentation-sqlalchemy/src/opentelemetry/instrumentation/sqlalchemy/engine.py @@ -79,15 +79,16 @@ def __init__(self, tracer, service, engine): def _before_cur_exec(self, conn, cursor, statement, *args): self.current_span = self.tracer.start_span(self.name) with self.tracer.use_span(self.current_span, end_on_exit=False): - self.current_span.set_attribute("service", self.vendor) - self.current_span.set_attribute(_STMT, statement) + if self.current_span.is_recording(): + self.current_span.set_attribute("service", self.vendor) + self.current_span.set_attribute(_STMT, statement) - if not _set_attributes_from_url( - self.current_span, conn.engine.url - ): - _set_attributes_from_cursor( - self.current_span, self.vendor, cursor - ) + if not _set_attributes_from_url( + self.current_span, conn.engine.url + ): + _set_attributes_from_cursor( + self.current_span, self.vendor, cursor + ) # pylint: disable=unused-argument def _after_cur_exec(self, conn, cursor, statement, *args): @@ -95,7 +96,11 @@ def _after_cur_exec(self, conn, cursor, statement, *args): return try: - if cursor and cursor.rowcount >= 0: + if ( + cursor + and cursor.rowcount >= 0 + and self.current_span.is_recording() + ): self.current_span.set_attribute(_ROWS, cursor.rowcount) finally: self.current_span.end() @@ -105,30 +110,34 @@ def _handle_error(self, context): return try: - self.current_span.set_status( - Status( - StatusCanonicalCode.UNKNOWN, - str(context.original_exception), + if self.current_span.is_recording(): + self.current_span.set_status( + Status( + StatusCanonicalCode.UNKNOWN, + str(context.original_exception), + ) ) - ) finally: self.current_span.end() def _set_attributes_from_url(span: trace.Span, url): """Set connection tags from the url. return true if successful.""" - if url.host: - span.set_attribute(_HOST, url.host) - if url.port: - span.set_attribute(_PORT, url.port) - if url.database: - span.set_attribute(_DB, url.database) + if span.is_recording(): + if url.host: + span.set_attribute(_HOST, url.host) + if url.port: + span.set_attribute(_PORT, url.port) + if url.database: + span.set_attribute(_DB, url.database) return bool(url.host) def _set_attributes_from_cursor(span: trace.Span, vendor, cursor): """Attempt to set db connection attributes by introspecting the cursor.""" + if not span.is_recording(): + return if vendor == "postgres": # pylint: disable=import-outside-toplevel from psycopg2.extensions import parse_dsn diff --git a/instrumentation/opentelemetry-instrumentation-sqlalchemy/tests/test_sqlalchemy.py b/instrumentation/opentelemetry-instrumentation-sqlalchemy/tests/test_sqlalchemy.py index 06593da94fd..3b2e3548a5c 100644 --- a/instrumentation/opentelemetry-instrumentation-sqlalchemy/tests/test_sqlalchemy.py +++ b/instrumentation/opentelemetry-instrumentation-sqlalchemy/tests/test_sqlalchemy.py @@ -11,6 +11,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +from unittest import mock from sqlalchemy import create_engine @@ -37,6 +38,28 @@ def test_trace_integration(self): self.assertEqual(len(spans), 1) self.assertEqual(spans[0].name, "sqlite.query") + 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 + mock_tracer.use_span.return_value.__enter__ = mock_span + mock_tracer.use_span.return_value.__exit__ = True + with mock.patch("opentelemetry.trace.get_tracer") as tracer: + tracer.return_value = mock_tracer + engine = create_engine("sqlite:///:memory:") + SQLAlchemyInstrumentor().instrument( + engine=engine, + tracer_provider=self.tracer_provider, + service="my-database", + ) + cnx = engine.connect() + cnx.execute("SELECT 1 + 1;").fetchall() + 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_create_engine_wrapper(self): SQLAlchemyInstrumentor().instrument() from sqlalchemy import create_engine # pylint: disable-all diff --git a/instrumentation/opentelemetry-instrumentation-tornado/tests/test_instrumentation.py b/instrumentation/opentelemetry-instrumentation-tornado/tests/test_instrumentation.py index eb2852f1123..5b429766ec3 100644 --- a/instrumentation/opentelemetry-instrumentation-tornado/tests/test_instrumentation.py +++ b/instrumentation/opentelemetry-instrumentation-tornado/tests/test_instrumentation.py @@ -138,7 +138,7 @@ def test_not_recording(self): mock_span.is_recording.return_value = False mock_tracer.start_span.return_value = mock_span mock_tracer.use_span.return_value.__enter__ = mock_span - mock_tracer.use_span.return_value.__exit__ = mock_span + mock_tracer.use_span.return_value.__exit__ = True with patch("opentelemetry.trace.get_tracer") as tracer: tracer.return_value = mock_tracer self.fetch("/")