Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
ff6539b
Fix psycopg2 (un)instrument_connection to use weakref, not mutate object
tammy-baylis-swi Feb 24, 2026
ba2e5ce
Changelog
tammy-baylis-swi Feb 24, 2026
5acf499
conditional import for docs types
tammy-baylis-swi Feb 24, 2026
1aa699d
Lint
tammy-baylis-swi Feb 24, 2026
6d10d23
Merge branch 'main' into psycopg2-connections-weakref-not-mutate
tammy-baylis-swi Feb 25, 2026
1ad519d
(v2) enable_commenter for instrument_connection psycopg(2)
tammy-baylis-swi Feb 26, 2026
cff1bb2
Adjust test trace flags
tammy-baylis-swi Feb 26, 2026
cbb0784
Merge branch 'main' into psycopg2-connections-weakref-not-mutate
tammy-baylis-swi Feb 26, 2026
e2b2bca
Merge branch 'main' into v2-enable-commenter-instrument-connection
tammy-baylis-swi Feb 26, 2026
f946c3b
Changelog
tammy-baylis-swi Feb 26, 2026
64fff71
SImplify test
tammy-baylis-swi Feb 27, 2026
a91c869
Merge branch 'psycopg2-connections-weakref-not-mutate' into v2-enable…
tammy-baylis-swi Feb 27, 2026
85d5f46
lint
tammy-baylis-swi Feb 27, 2026
ddc1bbd
Simplify
tammy-baylis-swi Feb 27, 2026
eed5984
Fix docs
tammy-baylis-swi Feb 27, 2026
8f7e4ea
Merge branch 'psycopg2-connections-weakref-not-mutate' into v2-enable…
tammy-baylis-swi Feb 27, 2026
dc070f5
Merge branch 'main' into v2-enable-commenter-instrument-connection
tammy-baylis-swi Mar 3, 2026
36635b7
Merge branch 'main' into v2-enable-commenter-instrument-connection
tammy-baylis-swi Mar 5, 2026
d5aced2
Merge branch 'main' into v2-enable-commenter-instrument-connection
tammy-baylis-swi Mar 19, 2026
a2fab21
Changelog
tammy-baylis-swi Mar 19, 2026
6624bb3
Merge branch 'main' into v2-enable-commenter-instrument-connection
tammy-baylis-swi Apr 6, 2026
da7822a
Merge branch 'main' into v2-enable-commenter-instrument-connection
tammy-baylis-swi Apr 7, 2026
df7feb6
Merge branch 'main' into v2-enable-commenter-instrument-connection
tammy-baylis-swi Apr 13, 2026
ae22b6e
Merge branch 'main' into v2-enable-commenter-instrument-connection
tammy-baylis-swi Apr 17, 2026
f7f27c2
Merge branch 'main' into v2-enable-commenter-instrument-connection
tammy-baylis-swi Apr 20, 2026
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 @@ -131,6 +131,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
([#4210](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4210))
- Add stale PR GitHub Action
([#4220](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4220))
- `opentelemetry-instrumentation-psycopg2`, `opentelemetry-instrumentation-psycopg`: Add sqlcommenter support for `instrument_connection`
([#4267](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4267/))

### Fixed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@
from opentelemetry.instrumentation.psycopg import PsycopgInstrumentor

PsycopgInstrumentor().instrument(enable_commenter=True)
# OR with specific connection
cnx = psycopg.connect(database='Database')
instrumented_cnx = PsycopgInstrumentor().instrument_connection(
cnx,
enable_commenter=True,
)


SQLCommenter with commenter_options
Expand Down Expand Up @@ -247,7 +253,11 @@ def _uninstrument(self, **kwargs: Any):
# TODO(owais): check if core dbapi can do this for all dbapi implementations e.g, pymysql and mysql
@staticmethod
def instrument_connection(
connection: ConnectionT, tracer_provider: TracerProvider | None = None
connection: ConnectionT,
tracer_provider: TracerProvider | None = None,
enable_commenter: bool = False,
commenter_options: dict = None,
enable_attribute_commenter: bool = False,
) -> ConnectionT:
"""Enable instrumentation in a psycopg connection.

Expand All @@ -257,6 +267,12 @@ def instrument_connection(
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.
enable_attribute_commenter:
Optional flag to enable/disable addition of sqlcomment to span attribute (default False). Requires enable_commenter=True.

Returns:
An instrumented psycopg connection object.
Expand All @@ -270,11 +286,17 @@ def instrument_connection(
)
if isinstance(connection, psycopg.AsyncConnection):
connection.cursor_factory = _new_cursor_async_factory(
tracer_provider=tracer_provider
tracer_provider=tracer_provider,
enable_commenter=enable_commenter,
commenter_options=commenter_options,
enable_attribute_commenter=enable_attribute_commenter,
)
else:
connection.cursor_factory = _new_cursor_factory(
tracer_provider=tracer_provider
tracer_provider=tracer_provider,
enable_commenter=enable_commenter,
commenter_options=commenter_options,
enable_attribute_commenter=enable_attribute_commenter,
)
connection._is_instrumented_by_opentelemetry = True
else:
Expand Down Expand Up @@ -362,6 +384,9 @@ def _new_cursor_factory(
db_api: DatabaseApiIntegration | None = None,
base_factory: type[psycopg.Cursor] | None = None,
tracer_provider: TracerProvider | None = None,
enable_commenter: bool = False,
commenter_options: dict = None,
enable_attribute_commenter: bool = False,
):
if not db_api:
db_api = DatabaseApiIntegration(
Expand All @@ -370,6 +395,10 @@ def _new_cursor_factory(
connection_attributes=PsycopgInstrumentor._CONNECTION_ATTRIBUTES,
version=__version__,
tracer_provider=tracer_provider,
enable_commenter=enable_commenter,
commenter_options=commenter_options,
connect_module=psycopg,
enable_attribute_commenter=enable_attribute_commenter,
)

base_factory = base_factory or psycopg.Cursor
Expand Down Expand Up @@ -398,6 +427,9 @@ def _new_cursor_async_factory(
db_api: DatabaseApiAsyncIntegration | None = None,
base_factory: type[psycopg.AsyncCursor] | None = None,
tracer_provider: TracerProvider | None = None,
enable_commenter: bool = False,
commenter_options: dict = None,
enable_attribute_commenter: bool = False,
):
if not db_api:
db_api = DatabaseApiAsyncIntegration(
Expand All @@ -406,6 +438,10 @@ def _new_cursor_async_factory(
connection_attributes=PsycopgInstrumentor._CONNECTION_ATTRIBUTES,
version=__version__,
tracer_provider=tracer_provider,
enable_commenter=enable_commenter,
commenter_options=commenter_options,
connect_module=psycopg,
enable_attribute_commenter=enable_attribute_commenter,
)
base_factory = base_factory or psycopg.AsyncCursor
_cursor_tracer = CursorTracer(db_api)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
import opentelemetry.instrumentation.psycopg
from opentelemetry.instrumentation.psycopg import PsycopgInstrumentor
from opentelemetry.sdk import resources
from opentelemetry.semconv._incubating.attributes.db_attributes import (
DB_STATEMENT,
)
from opentelemetry.test.test_base import TestBase


Expand Down Expand Up @@ -451,6 +454,152 @@ 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.pq.version",
new=lambda: "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")
trace_flags = int(span.get_span_context().trace_flags)
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}-{trace_flags:02x}'*/",
)
self.assertEqual(
span.attributes[DB_STATEMENT],
"Select 1",
)

def test_sqlcommenter_enabled_instrument_connection_stmt_enabled(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.pq.version",
new=lambda: "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,
enable_attribute_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")
trace_flags = int(span.get_span_context().trace_flags)
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}-{trace_flags:02x}'*/",
)
self.assertEqual(
span.attributes[DB_STATEMENT],
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}-{trace_flags:02x}'*/",
)

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.pq.version",
new=lambda: "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")
trace_flags = int(span.get_span_context().trace_flags)
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}-{trace_flags:02x}'*/",
)
self.assertEqual(
span.attributes[DB_STATEMENT],
"Select 1",
)

@mock.patch("opentelemetry.instrumentation.dbapi.wrap_connect")
def test_sqlcommenter_disabled(self, event_mocked):
cnx = psycopg.connect(database="test")
Expand All @@ -461,6 +610,45 @@ 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",
)
spans_list = self.memory_exporter.get_finished_spans()
span = spans_list[0]
self.assertEqual(
span.attributes[DB_STATEMENT],
"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",
)
spans_list = self.memory_exporter.get_finished_spans()
span = spans_list[0]
self.assertEqual(
span.attributes[DB_STATEMENT],
"Select 1",
)


class TestPostgresqlIntegrationAsync(
PostgresqlIntegrationTestMixin, TestBase, IsolatedAsyncioTestCase
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@

Psycopg2Instrumentor().instrument(enable_commenter=True)

# OR with specific connection
cnx = psycopg2.connect(database='Database')
instrumented_cnx = Psycopg2Instrumentor().instrument_connection(
cnx,
enable_commenter=True,
)

SQLCommenter with commenter_options
***********************************
Expand Down Expand Up @@ -251,6 +257,9 @@ def _uninstrument(self, **kwargs):
def instrument_connection(
connection: PgConnection,
tracer_provider: typing.Optional[trace_api.TracerProvider] = None,
enable_commenter: bool = False,
commenter_options: dict = None,
enable_attribute_commenter: bool = False,
) -> PgConnection:
"""Enable instrumentation in a psycopg2 connection.

Expand All @@ -263,6 +272,12 @@ def instrument_connection(
tracer_provider: opentelemetry.trace.TracerProvider, optional
The TracerProvider to use for instrumentation. If not specified,
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.
enable_attribute_commenter:
Optional flag to enable/disable addition of sqlcomment to span attribute (default False). Requires enable_commenter=True.

Returns:
An instrumented psycopg2 connection object.
Expand All @@ -279,6 +294,9 @@ def instrument_connection(
connection.cursor_factory = _new_cursor_factory(
base_factory=original_cursor_factory,
tracer_provider=tracer_provider,
enable_commenter=enable_commenter,
commenter_options=commenter_options,
enable_attribute_commenter=enable_attribute_commenter,
)
Psycopg2Instrumentor._INSTRUMENTED_CONNECTIONS[connection] = (
original_cursor_factory
Expand Down Expand Up @@ -348,14 +366,25 @@ 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: typing.Optional[typing.Type[pg_cursor]] = None,
tracer_provider: typing.Optional[trace_api.TracerProvider] = None,
enable_commenter: bool = False,
commenter_options: dict = None,
enable_attribute_commenter: bool = False,
):
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,
enable_attribute_commenter=enable_attribute_commenter,
)

base_factory = base_factory or pg_cursor
Expand Down
Loading
Loading