diff --git a/CHANGELOG.md b/CHANGELOG.md index d33472c8e6..e64b292766 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#3777](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3777)) - `opentelemetry-exporter-credential-provider-gcp`: create this package which provides support for supplying your machine's Application Default Credentials (https://cloud.google.com/docs/authentication/application-default-credentials) to the OTLP Exporters created automatically by OpenTelemetry Python's auto instrumentation. These credentials authorize OTLP traces to be sent to `telemetry.googleapis.com`. [#3766](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3766). +- `opentelemetry-instrumentation-psycopg` Add missing parameter `capture_parameters` to instrumentor. + ([#3676](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3676)) ## Version 1.37.0/0.58b0 (2025-09-11) diff --git a/instrumentation/opentelemetry-instrumentation-psycopg/src/opentelemetry/instrumentation/psycopg/__init__.py b/instrumentation/opentelemetry-instrumentation-psycopg/src/opentelemetry/instrumentation/psycopg/__init__.py index d2c4880650..e3c85ccf16 100644 --- a/instrumentation/opentelemetry-instrumentation-psycopg/src/opentelemetry/instrumentation/psycopg/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-psycopg/src/opentelemetry/instrumentation/psycopg/__init__.py @@ -184,6 +184,7 @@ def _instrument(self, **kwargs: Any): enable_attribute_commenter = kwargs.get( "enable_attribute_commenter", False ) + capture_parameters = kwargs.get("capture_parameters", False) dbapi.wrap_connect( __name__, psycopg, @@ -196,6 +197,7 @@ def _instrument(self, **kwargs: Any): enable_commenter=enable_sqlcommenter, commenter_options=commenter_options, enable_attribute_commenter=enable_attribute_commenter, + capture_parameters=capture_parameters, ) dbapi.wrap_connect( @@ -210,6 +212,7 @@ def _instrument(self, **kwargs: Any): enable_commenter=enable_sqlcommenter, commenter_options=commenter_options, enable_attribute_commenter=enable_attribute_commenter, + capture_parameters=capture_parameters, ) dbapi.wrap_connect( __name__, @@ -223,6 +226,7 @@ def _instrument(self, **kwargs: Any): enable_commenter=enable_sqlcommenter, commenter_options=commenter_options, enable_attribute_commenter=enable_attribute_commenter, + capture_parameters=capture_parameters, ) def _uninstrument(self, **kwargs: Any): diff --git a/instrumentation/opentelemetry-instrumentation-psycopg/tests/test_psycopg_integration.py b/instrumentation/opentelemetry-instrumentation-psycopg/tests/test_psycopg_integration.py index 0987d3e932..da43fbad32 100644 --- a/instrumentation/opentelemetry-instrumentation-psycopg/tests/test_psycopg_integration.py +++ b/instrumentation/opentelemetry-instrumentation-psycopg/tests/test_psycopg_integration.py @@ -264,6 +264,24 @@ def test_span_name(self): self.assertEqual(spans_list[6].name, "postgresql") self.assertEqual(spans_list[7].name, "--") + def test_span_params_attribute(self): + PsycopgInstrumentor().instrument(capture_parameters=True) + cnx = psycopg.connect(database="test") + query = "SELECT * FROM mytable WHERE myparam1 = %s AND myparam2 = %s" + params = ("test", 42) + + cursor = cnx.cursor() + + cursor.execute(query, params) + spans_list = self.memory_exporter.get_finished_spans() + self.assertEqual(len(spans_list), 1) + self.assertEqual(spans_list[0].name, "SELECT") + assert spans_list[0].attributes is not None + self.assertEqual(spans_list[0].attributes["db.statement"], query) + self.assertEqual( + spans_list[0].attributes["db.statement.parameters"], str(params) + ) + # pylint: disable=unused-argument def test_not_recording(self): mock_tracer = mock.Mock() @@ -479,6 +497,23 @@ async def test_span_name_async(self): self.assertEqual(spans_list[4].name, "query") self.assertEqual(spans_list[5].name, "query") + async def test_span_params_attribute(self): + PsycopgInstrumentor().instrument(capture_parameters=True) + cnx = await psycopg.AsyncConnection.connect("test") + query = "SELECT * FROM mytable WHERE myparam1 = %s AND myparam2 = %s" + params = ("test", 42) + async with cnx.cursor() as cursor: + await cursor.execute(query, params) + + spans_list = self.memory_exporter.get_finished_spans() + self.assertEqual(len(spans_list), 1) + self.assertEqual(spans_list[0].name, "SELECT") + assert spans_list[0].attributes is not None + self.assertEqual(spans_list[0].attributes["db.statement"], query) + self.assertEqual( + spans_list[0].attributes["db.statement.parameters"], str(params) + ) + # pylint: disable=unused-argument async def test_not_recording_async(self): mock_tracer = mock.Mock()