106106from typing import Collection
107107
108108import psycopg2
109+ import wrapt
109110from psycopg2 .extensions import (
110111 cursor as pg_cursor , # pylint: disable=no-name-in-module
111112)
112113from psycopg2 .sql import Composed # pylint: disable=no-name-in-module
113114
115+ from opentelemetry import trace as trace_api
114116from opentelemetry .instrumentation import dbapi
115117from opentelemetry .instrumentation .instrumentor import BaseInstrumentor
116118from opentelemetry .instrumentation .psycopg2 .package import _instruments
117119from opentelemetry .instrumentation .psycopg2 .version import __version__
118120
119121_logger = logging .getLogger (__name__ )
120- _OTEL_CURSOR_FACTORY_KEY = "_otel_orig_cursor_factory"
121122
122123
123124class Psycopg2Instrumentor (BaseInstrumentor ):
@@ -130,6 +131,8 @@ class Psycopg2Instrumentor(BaseInstrumentor):
130131
131132 _DATABASE_SYSTEM = "postgresql"
132133
134+ _otel_cursor_factory = None
135+
133136 def instrumentation_dependencies (self ) -> Collection [str ]:
134137 return _instruments
135138
@@ -158,32 +161,40 @@ def _uninstrument(self, **kwargs):
158161 dbapi .unwrap_connect (psycopg2 , "connect" )
159162
160163 # TODO(owais): check if core dbapi can do this for all dbapi implementations e.g, pymysql and mysql
161- @staticmethod
162- def instrument_connection (connection , tracer_provider = None ):
163- if not hasattr (connection , "_is_instrumented_by_opentelemetry" ):
164- connection ._is_instrumented_by_opentelemetry = False
165-
166- if not connection ._is_instrumented_by_opentelemetry :
167- setattr (
168- connection , _OTEL_CURSOR_FACTORY_KEY , connection .cursor_factory
169- )
170- connection .cursor_factory = _new_cursor_factory (
171- tracer_provider = tracer_provider
172- )
173- connection ._is_instrumented_by_opentelemetry = True
174- else :
164+ def instrument_connection (
165+ self ,
166+ connection ,
167+ tracer_provider : typing .Optional [trace_api .TracerProvider ] = None ,
168+ enable_commenter : bool = False ,
169+ commenter_options : dict = None ,
170+ ):
171+ if isinstance (connection , wrapt .ObjectProxy ):
172+ # The connection is already instrumented from wrapt.wrap_function_wrapper
173+ # of the psycopg2 module's `connect` method by DB-API `wrap_connect`,
174+ # so the Psycopg2Instrumentor is marked as instrumenting.
175175 _logger .warning (
176176 "Attempting to instrument Psycopg connection while already instrumented"
177177 )
178+ self ._is_instrumented_by_opentelemetry = True
179+ return connection
180+
181+ # Save cursor_factory at instrumentor level because
182+ # psycopg2 connection type does not allow arbitrary attrs
183+ self ._otel_cursor_factory = connection .cursor_factory
184+ connection .cursor_factory = _new_cursor_factory (
185+ base_factory = connection .cursor_factory ,
186+ tracer_provider = tracer_provider ,
187+ enable_commenter = enable_commenter ,
188+ commenter_options = commenter_options ,
189+ )
190+ self ._is_instrumented_by_opentelemetry = True
191+
178192 return connection
179193
180194 # TODO(owais): check if core dbapi can do this for all dbapi implementations e.g, pymysql and mysql
181- @staticmethod
182- def uninstrument_connection (connection ):
183- connection .cursor_factory = getattr (
184- connection , _OTEL_CURSOR_FACTORY_KEY , None
185- )
186-
195+ def uninstrument_connection (self , connection ):
196+ self ._is_instrumented_by_opentelemetry = False
197+ connection .cursor_factory = self ._otel_cursor_factory
187198 return connection
188199
189200
@@ -231,14 +242,23 @@ def get_statement(self, cursor, args):
231242 return statement
232243
233244
234- def _new_cursor_factory (db_api = None , base_factory = None , tracer_provider = None ):
245+ def _new_cursor_factory (
246+ db_api : dbapi .DatabaseApiIntegration = None ,
247+ base_factory : pg_cursor = None ,
248+ tracer_provider : typing .Optional [trace_api .TracerProvider ] = None ,
249+ enable_commenter : bool = False ,
250+ commenter_options : dict = None ,
251+ ):
235252 if not db_api :
236253 db_api = DatabaseApiIntegration (
237254 __name__ ,
238255 Psycopg2Instrumentor ._DATABASE_SYSTEM ,
239256 connection_attributes = Psycopg2Instrumentor ._CONNECTION_ATTRIBUTES ,
240257 version = __version__ ,
241258 tracer_provider = tracer_provider ,
259+ enable_commenter = enable_commenter ,
260+ commenter_options = commenter_options ,
261+ connect_module = psycopg2 ,
242262 )
243263
244264 base_factory = base_factory or pg_cursor
0 commit comments