Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support enable_commenter for instrument_connection by psycopg(2 #3071

Open
wants to merge 19 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
d2da8cd
enable_commenter for instrument_connection psycopg(2)
tammy-baylis-swi Dec 5, 2024
4783f3a
Changelog
tammy-baylis-swi Dec 5, 2024
bf49c8c
Add psycopg2 tests
tammy-baylis-swi Dec 7, 2024
e148c9f
Add psycopg tests
tammy-baylis-swi Dec 7, 2024
58daeb6
Update typehints
tammy-baylis-swi Dec 7, 2024
8e8b759
Document psycopg(2) instrument_connection
tammy-baylis-swi Dec 7, 2024
e3d7941
Consistent pg_connection alias usage
tammy-baylis-swi Dec 11, 2024
a94a591
Revert "Consistent pg_connection alias usage"
tammy-baylis-swi Dec 11, 2024
0e24d6d
Use qualified import not alias
tammy-baylis-swi Dec 11, 2024
da529bd
Fix psycopg2/3 docs
tammy-baylis-swi Dec 11, 2024
197e5ea
Merge branch 'main' into enable-commenter-instrument-connection
tammy-baylis-swi Dec 11, 2024
b3ae9c8
Merge branch 'main' into enable-commenter-instrument-connection
tammy-baylis-swi Dec 11, 2024
64941f9
Merge branch 'main' into enable-commenter-instrument-connection
tammy-baylis-swi Dec 19, 2024
e6ca8fe
Merge branch 'main' into enable-commenter-instrument-connection
tammy-baylis-swi Jan 2, 2025
84d82d0
Fix
tammy-baylis-swi Jan 2, 2025
65ac19a
Merge branch 'main' into enable-commenter-instrument-connection
tammy-baylis-swi Jan 7, 2025
1ca0cd0
Merge branch 'main' into enable-commenter-instrument-connection
tammy-baylis-swi Jan 8, 2025
afff465
Merge branch 'main' into enable-commenter-instrument-connection
tammy-baylis-swi Jan 10, 2025
fd11f1a
Merge branch 'main' into enable-commenter-instrument-connection
tammy-baylis-swi Jan 13, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
([#2942](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2942))
- `opentelemetry-instrumentation-click`: new instrumentation to trace click commands
([#2994](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2994))
- `opentelemetry-instrumentation-psycopg2`, `opentelemetry-instrumentation-psycopg`: Add sqlcommenter support for `instrument_connection`
([#3071](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3071))

### Fixed

Expand Down
1 change: 1 addition & 0 deletions docs-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ mysqlclient~=2.1.1
openai >= 1.26.0
psutil>=5
psycopg~=3.1.17
psycopg2~=2.9.9
pika>=0.12.0
pymongo~=4.6.3
PyMySQL~=1.1.1
Expand Down
2 changes: 2 additions & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@
"https://opentelemetry-python.readthedocs.io/en/latest/",
None,
),
"psycopg": ("https://www.psycopg.org/psycopg3/docs/", None),
"psycopg2": ("https://www.psycopg.org/docs/", None),
}

# http://www.sphinx-doc.org/en/master/config.html#confval-nitpicky
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
The integration with PostgreSQL supports the `Psycopg`_ library, it can be enabled by
using ``PsycopgInstrumentor``.

.. _Psycopg: http://initd.org/psycopg/
.. _Psycopg: https://www.psycopg.org/psycopg3/docs/

SQLCOMMENTER
*****************************************
Expand Down Expand Up @@ -150,6 +150,7 @@
)
from psycopg.sql import Composed # pylint: disable=no-name-in-module

from opentelemetry import trace as trace_api
from opentelemetry.instrumentation import dbapi
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
from opentelemetry.instrumentation.psycopg.package import _instruments
Expand All @@ -174,7 +175,7 @@ def instrumentation_dependencies(self) -> Collection[str]:

def _instrument(self, **kwargs):
"""Integrate with PostgreSQL Psycopg library.
Psycopg: http://initd.org/psycopg/
Psycopg: https://www.psycopg.org/psycopg3/docs/
"""
tracer_provider = kwargs.get("tracer_provider")
enable_sqlcommenter = kwargs.get("enable_commenter", False)
Expand Down Expand Up @@ -237,15 +238,24 @@ def _uninstrument(self, **kwargs):

# TODO(owais): check if core dbapi can do this for all dbapi implementations e.g, pymysql and mysql
@staticmethod
def instrument_connection(connection, tracer_provider=None):
"""Enable instrumentation in a psycopg connection.
def instrument_connection(
connection: psycopg.Connection,
tracer_provider: typing.Optional[trace_api.TracerProvider] = None,
enable_commenter: bool = False,
commenter_options: dict = None,
):
"""Enable instrumentation of a Psycopg connection.

Args:
connection: psycopg.Connection
The psycopg connection object to be instrumented.
tracer_provider: opentelemetry.trace.TracerProvider, optional
The TracerProvider to use for instrumentation. If not provided,
the global TracerProvider will be used.
enable_commenter: bool, optional
Optional flag to enable/disable sqlcommenter (default False).
commenter_options: dict, optional
Optional configurations for tags to be appended at the sql query.

Returns:
An instrumented psycopg connection object.
Expand All @@ -258,7 +268,9 @@ def instrument_connection(connection, tracer_provider=None):
connection, _OTEL_CURSOR_FACTORY_KEY, connection.cursor_factory
)
connection.cursor_factory = _new_cursor_factory(
tracer_provider=tracer_provider
tracer_provider=tracer_provider,
enable_commenter=enable_commenter,
commenter_options=commenter_options,
)
connection._is_instrumented_by_opentelemetry = True
else:
Expand Down Expand Up @@ -342,14 +354,23 @@ def get_statement(self, cursor, args):
return statement


def _new_cursor_factory(db_api=None, base_factory=None, tracer_provider=None):
def _new_cursor_factory(
db_api: dbapi.DatabaseApiIntegration = None,
base_factory: pg_cursor = None,
tracer_provider: typing.Optional[trace_api.TracerProvider] = None,
enable_commenter: bool = False,
commenter_options: dict = None,
):
if not db_api:
db_api = DatabaseApiIntegration(
__name__,
PsycopgInstrumentor._DATABASE_SYSTEM,
connection_attributes=PsycopgInstrumentor._CONNECTION_ATTRIBUTES,
version=__version__,
tracer_provider=tracer_provider,
enable_commenter=enable_commenter,
commenter_options=commenter_options,
connect_module=psycopg,
)

base_factory = base_factory or pg_cursor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,74 @@ def test_sqlcommenter_enabled(self, event_mocked):
kwargs = event_mocked.call_args[1]
self.assertEqual(kwargs["enable_commenter"], True)

def test_sqlcommenter_enabled_instrument_connection_defaults(self):
with mock.patch(
"opentelemetry.instrumentation.psycopg.psycopg.__version__",
"foobar",
), mock.patch(
"opentelemetry.instrumentation.psycopg.psycopg.pq.__build_version__",
"foobaz",
), mock.patch(
"opentelemetry.instrumentation.psycopg.psycopg.threadsafety",
"123",
), mock.patch(
"opentelemetry.instrumentation.psycopg.psycopg.apilevel",
"123",
), mock.patch(
"opentelemetry.instrumentation.psycopg.psycopg.paramstyle",
"test",
):
cnx = psycopg.connect(database="test")
cnx = PsycopgInstrumentor().instrument_connection(
cnx,
enable_commenter=True,
)
query = "Select 1"
cursor = cnx.cursor()
cursor.execute(query)
spans_list = self.memory_exporter.get_finished_spans()
span = spans_list[0]
span_id = format(span.get_span_context().span_id, "016x")
trace_id = format(span.get_span_context().trace_id, "032x")
self.assertEqual(
MockCursor.execute.call_args[0][0],
f"Select 1 /*db_driver='psycopg%%3Afoobar',dbapi_level='123',dbapi_threadsafety='123',driver_paramstyle='test',libpq_version='foobaz',traceparent='00-{trace_id}-{span_id}-01'*/",
)

def test_sqlcommenter_enabled_instrument_connection_with_options(self):
with mock.patch(
"opentelemetry.instrumentation.psycopg.psycopg.__version__",
"foobar",
), mock.patch(
"opentelemetry.instrumentation.psycopg.psycopg.pq.__build_version__",
"foobaz",
), mock.patch(
"opentelemetry.instrumentation.psycopg.psycopg.threadsafety",
"123",
):
cnx = psycopg.connect(database="test")
cnx = PsycopgInstrumentor().instrument_connection(
cnx,
enable_commenter=True,
commenter_options={
"dbapi_level": False,
"dbapi_threadsafety": True,
"driver_paramstyle": False,
"foo": "ignored",
},
)
query = "Select 1"
cursor = cnx.cursor()
cursor.execute(query)
spans_list = self.memory_exporter.get_finished_spans()
span = spans_list[0]
span_id = format(span.get_span_context().span_id, "016x")
trace_id = format(span.get_span_context().trace_id, "032x")
self.assertEqual(
MockCursor.execute.call_args[0][0],
f"Select 1 /*db_driver='psycopg%%3Afoobar',dbapi_threadsafety='123',libpq_version='foobaz',traceparent='00-{trace_id}-{span_id}-01'*/",
)

@mock.patch("opentelemetry.instrumentation.dbapi.wrap_connect")
def test_sqlcommenter_disabled(self, event_mocked):
cnx = psycopg.connect(database="test")
Expand All @@ -388,6 +456,33 @@ def test_sqlcommenter_disabled(self, event_mocked):
kwargs = event_mocked.call_args[1]
self.assertEqual(kwargs["enable_commenter"], False)

def test_sqlcommenter_disabled_default_instrument_connection(self):
cnx = psycopg.connect(database="test")
cnx = PsycopgInstrumentor().instrument_connection(
cnx,
)
query = "Select 1"
cursor = cnx.cursor()
cursor.execute(query)
self.assertEqual(
MockCursor.execute.call_args[0][0],
"Select 1",
)

def test_sqlcommenter_disabled_explicit_instrument_connection(self):
cnx = psycopg.connect(database="test")
cnx = PsycopgInstrumentor().instrument_connection(
cnx,
enable_commenter=False,
)
query = "Select 1"
cursor = cnx.cursor()
cursor.execute(query)
self.assertEqual(
MockCursor.execute.call_args[0][0],
"Select 1",
)


class TestPostgresqlIntegrationAsync(
PostgresqlIntegrationTestMixin, TestBase, IsolatedAsyncioTestCase
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@
# limitations under the License.

"""
The integration with PostgreSQL supports the `Psycopg`_ library, it can be enabled by
The integration with PostgreSQL supports the `Psycopg2`_ library, it can be enabled by
using ``Psycopg2Instrumentor``.

.. _Psycopg: http://initd.org/psycopg/
.. _Psycopg2: https://www.psycopg.org/docs/

SQLCOMMENTER
*****************************************
Expand Down Expand Up @@ -142,11 +142,15 @@
from typing import Collection

import psycopg2
from psycopg2.extensions import (
connection as pg_connection, # pylint: disable=no-name-in-module
)
from psycopg2.extensions import (
cursor as pg_cursor, # pylint: disable=no-name-in-module
)
from psycopg2.sql import Composed # pylint: disable=no-name-in-module

from opentelemetry import trace as trace_api
from opentelemetry.instrumentation import dbapi
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
from opentelemetry.instrumentation.psycopg2.package import _instruments
Expand All @@ -170,8 +174,8 @@ def instrumentation_dependencies(self) -> Collection[str]:
return _instruments

def _instrument(self, **kwargs):
"""Integrate with PostgreSQL Psycopg library.
Psycopg: http://initd.org/psycopg/
"""Integrate with PostgreSQL Psycopg2 library.
Psycopg2: https://www.psycopg.org/docs/
"""
tracer_provider = kwargs.get("tracer_provider")
enable_sqlcommenter = kwargs.get("enable_commenter", False)
Expand Down Expand Up @@ -199,20 +203,28 @@ def _uninstrument(self, **kwargs):

# TODO(owais): check if core dbapi can do this for all dbapi implementations e.g, pymysql and mysql
@staticmethod
def instrument_connection(connection, tracer_provider=None):
"""Enable instrumentation in a psycopg2 connection.
def instrument_connection(
connection: pg_connection,
tracer_provider: typing.Optional[trace_api.TracerProvider] = None,
enable_commenter: bool = False,
commenter_options: dict = None,
):
"""Enable instrumentation of a Psycopg2 connection.

Args:
connection: psycopg2.extensions.connection
The psycopg2 connection object to be instrumented.
tracer_provider: opentelemetry.trace.TracerProvider, optional
The TracerProvider to use for instrumentation. If not specified,
The TracerProvider to use for instrumentation. If not provided,
the global TracerProvider will be used.
enable_commenter: bool, optional
Optional flag to enable/disable sqlcommenter (default False).
commenter_options: dict, optional
Optional configurations for tags to be appended at the sql query.

Returns:
An instrumented psycopg2 connection object.
"""

if not hasattr(connection, "_is_instrumented_by_opentelemetry"):
connection._is_instrumented_by_opentelemetry = False

Expand All @@ -221,7 +233,9 @@ def instrument_connection(connection, tracer_provider=None):
connection, _OTEL_CURSOR_FACTORY_KEY, connection.cursor_factory
)
connection.cursor_factory = _new_cursor_factory(
tracer_provider=tracer_provider
tracer_provider=tracer_provider,
enable_commenter=enable_commenter,
commenter_options=commenter_options,
)
connection._is_instrumented_by_opentelemetry = True
else:
Expand Down Expand Up @@ -284,14 +298,23 @@ def get_statement(self, cursor, args):
return statement


def _new_cursor_factory(db_api=None, base_factory=None, tracer_provider=None):
def _new_cursor_factory(
db_api: dbapi.DatabaseApiIntegration = None,
base_factory: pg_cursor = None,
tracer_provider: typing.Optional[trace_api.TracerProvider] = None,
enable_commenter: bool = False,
commenter_options: dict = None,
):
if not db_api:
db_api = DatabaseApiIntegration(
__name__,
Psycopg2Instrumentor._DATABASE_SYSTEM,
connection_attributes=Psycopg2Instrumentor._CONNECTION_ATTRIBUTES,
version=__version__,
tracer_provider=tracer_provider,
enable_commenter=enable_commenter,
commenter_options=commenter_options,
connect_module=psycopg2,
)

base_factory = base_factory or pg_cursor
Expand Down
Loading
Loading