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 1d51d14d5b..3a28c0aab1 100644 --- a/instrumentation/opentelemetry-instrumentation-aiopg/src/opentelemetry/instrumentation/aiopg/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-aiopg/src/opentelemetry/instrumentation/aiopg/__init__.py @@ -25,22 +25,39 @@ import aiopg from opentelemetry.instrumentation.aiopg import AiopgInstrumentor - + # Call instrument() to wrap all database connections AiopgInstrumentor().instrument() cnx = await aiopg.connect(database='Database') cursor = await cnx.cursor() + await cursor.execute("CREATE TABLE IF NOT EXISTS test (testField INTEGER)") await cursor.execute("INSERT INTO test (testField) VALUES (123)") cursor.close() cnx.close() pool = await aiopg.create_pool(database='Database') + cnx = await pool.acquire() cursor = await cnx.cursor() + await cursor.execute("CREATE TABLE IF NOT EXISTS test (testField INTEGER)") await cursor.execute("INSERT INTO test (testField) VALUES (123)") cursor.close() cnx.close() +.. code-block:: python + + import aiopg + from opentelemetry.instrumentation.aiopg import AiopgInstrumentor + + # Alternatively, use instrument_connection for an individual connection + cnx = await aiopg.connect(database='Database') + instrumented_cnx = AiopgInstrumentor().instrument_connection(cnx) + cursor = await instrumented_cnx.cursor() + await cursor.execute("CREATE TABLE IF NOT EXISTS test (testField INTEGER)") + await cursor.execute("INSERT INTO test (testField) VALUES (123)") + cursor.close() + instrumented_cnx.close() + API --- """ diff --git a/instrumentation/opentelemetry-instrumentation-mysql/src/opentelemetry/instrumentation/mysql/__init__.py b/instrumentation/opentelemetry-instrumentation-mysql/src/opentelemetry/instrumentation/mysql/__init__.py index d88dffde2b..0116dab1c3 100644 --- a/instrumentation/opentelemetry-instrumentation-mysql/src/opentelemetry/instrumentation/mysql/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-mysql/src/opentelemetry/instrumentation/mysql/__init__.py @@ -26,14 +26,30 @@ import mysql.connector from opentelemetry.instrumentation.mysql import MySQLInstrumentor + # Call instrument() to wrap all database connections MySQLInstrumentor().instrument() cnx = mysql.connector.connect(database="MySQL_Database") cursor = cnx.cursor() + cursor.execute("CREATE TABLE IF NOT EXISTS test (testField INTEGER)") cursor.execute("INSERT INTO test (testField) VALUES (123)") cursor.close() cnx.close() +.. code:: python + + import mysql.connector + from opentelemetry.instrumentation.mysql import MySQLInstrumentor + + # Alternatively, use instrument_connection for an individual connection + cnx = mysql.connector.connect(database="MySQL_Database") + instrumented_cnx = MySQLInstrumentor().instrument_connection(cnx) + cursor = instrumented_cnx.cursor() + cursor.execute("CREATE TABLE IF NOT EXISTS test (testField INTEGER)") + cursor.execute("INSERT INTO test (testField) VALUES (123)") + cursor.close() + instrumented_cnx.close() + API --- """ @@ -86,12 +102,16 @@ def instrument_connection(self, connection, tracer_provider=None): """Enable instrumentation in a MySQL connection. Args: - connection: The connection to instrument. - tracer_provider: The optional tracer provider to use. If omitted - the current globally configured one is used. + connection: + The existing MySQL connection instance to instrument. This connection is typically + obtained through `mysql.connector.connect()` and is instrumented to collect telemetry + data about database interactions. + tracer_provider: + An optional `TracerProvider` instance to use for tracing. If not provided, the globally + configured tracer provider will be automatically used. Returns: - An instrumented connection. + An instrumented MySQL connection with OpenTelemetry tracing enabled. """ return dbapi.instrument_connection( __name__, diff --git a/instrumentation/opentelemetry-instrumentation-mysqlclient/src/opentelemetry/instrumentation/mysqlclient/__init__.py b/instrumentation/opentelemetry-instrumentation-mysqlclient/src/opentelemetry/instrumentation/mysqlclient/__init__.py index 5b08b0b50d..e1c07096fa 100644 --- a/instrumentation/opentelemetry-instrumentation-mysqlclient/src/opentelemetry/instrumentation/mysqlclient/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-mysqlclient/src/opentelemetry/instrumentation/mysqlclient/__init__.py @@ -46,16 +46,40 @@ import MySQLdb from opentelemetry.instrumentation.mysqlclient import MySQLClientInstrumentor - + # Call instrument() to wrap all database connections MySQLClientInstrumentor().instrument(enable_commenter=True, commenter_options={}) cnx = MySQLdb.connect(database="MySQL_Database") cursor = cnx.cursor() + cursor.execute("CREATE TABLE IF NOT EXISTS test (testField INTEGER)") cursor.execute("INSERT INTO test (testField) VALUES (123)" cnx.commit() cursor.close() cnx.close() +.. code:: python + + import MySQLdb + from opentelemetry.instrumentation.mysqlclient import MySQLClientInstrumentor + + # Alternatively, use instrument_connection for an individual connection + cnx = MySQLdb.connect(database="MySQL_Database") + instrumented_cnx = MySQLClientInstrumentor().instrument_connection( + cnx, + enable_commenter=True, + commenter_options={ + "db_driver": True, + "mysql_client_version": True, + "driver_paramstyle": False + } + ) + cursor = instrumented_cnx.cursor() + cursor.execute("CREATE TABLE IF NOT EXISTS test (testField INTEGER)") + cursor.execute("INSERT INTO test (testField) VALUES (123)" + instrumented_cnx.commit() + cursor.close() + instrumented_cnx.close() + For example, :: @@ -162,12 +186,28 @@ def instrument_connection( """Enable instrumentation in a mysqlclient connection. Args: - connection: The connection to instrument. - tracer_provider: The optional tracer provider to use. If omitted - the current globally configured one is used. - + connection: + The MySQL connection instance to instrument. This connection is typically + created using `MySQLdb.connect()` and needs to be wrapped to collect telemetry. + tracer_provider: + A custom `TracerProvider` instance to be used for tracing. If not specified, + the globally configured tracer provider will be used. + enable_commenter: + A flag to enable the OpenTelemetry SQLCommenter feature. If set to `True`, + SQL queries will be enriched with contextual information (e.g., database client details). + Default is `None`. + commenter_options: + A dictionary of configuration options for SQLCommenter. All options are enabled (True) by default. + This allows you to customize metadata appended to queries. Possible options include: + + - `db_driver`: Adds the database driver name and version. + - `dbapi_threadsafety`: Adds threadsafety information. + - `dbapi_level`: Adds the DB-API version. + - `mysql_client_version`: Adds the MySQL client version. + - `driver_paramstyle`: Adds the parameter style. + - `opentelemetry_values`: Includes traceparent values. Returns: - An instrumented connection. + An instrumented MySQL connection with OpenTelemetry support enabled. """ return dbapi.instrument_connection( 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 e986ec0d46..8c608b7655 100644 --- a/instrumentation/opentelemetry-instrumentation-psycopg/src/opentelemetry/instrumentation/psycopg/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-psycopg/src/opentelemetry/instrumentation/psycopg/__init__.py @@ -88,15 +88,31 @@ import psycopg from opentelemetry.instrumentation.psycopg import PsycopgInstrumentor - + # Call instrument() to wrap all database connections PsycopgInstrumentor().instrument() cnx = psycopg.connect(database='Database') + cursor = cnx.cursor() + cursor.execute("CREATE TABLE IF NOT EXISTS test (testField INTEGER)") cursor.execute("INSERT INTO test (testField) VALUES (123)") cursor.close() cnx.close() +.. code-block:: python + + import psycopg + from opentelemetry.instrumentation.psycopg import PsycopgInstrumentor + + # Alternatively, use instrument_connection for an individual connection + cnx = psycopg.connect(database='Database') + instrumented_cnx = PsycopgInstrumentor().instrument_connection(cnx) + cursor = instrumented_cnx.cursor() + cursor.execute("CREATE TABLE IF NOT EXISTS test (testField INTEGER)") + cursor.execute("INSERT INTO test (testField) VALUES (123)") + cursor.close() + instrumented_cnx.close() + API --- """ @@ -196,6 +212,18 @@ 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. + + 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. + + Returns: + An instrumented psycopg connection object. + """ if not hasattr(connection, "_is_instrumented_by_opentelemetry"): connection._is_instrumented_by_opentelemetry = False diff --git a/instrumentation/opentelemetry-instrumentation-psycopg2/src/opentelemetry/instrumentation/psycopg2/__init__.py b/instrumentation/opentelemetry-instrumentation-psycopg2/src/opentelemetry/instrumentation/psycopg2/__init__.py index de2e49f4c3..a811f4285a 100644 --- a/instrumentation/opentelemetry-instrumentation-psycopg2/src/opentelemetry/instrumentation/psycopg2/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-psycopg2/src/opentelemetry/instrumentation/psycopg2/__init__.py @@ -88,15 +88,31 @@ import psycopg2 from opentelemetry.instrumentation.psycopg2 import Psycopg2Instrumentor - + # Call instrument() to wrap all database connections Psycopg2Instrumentor().instrument() cnx = psycopg2.connect(database='Database') + cursor = cnx.cursor() + cursor.execute("CREATE TABLE IF NOT EXISTS test (testField INTEGER)") cursor.execute("INSERT INTO test (testField) VALUES (123)") cursor.close() cnx.close() +.. code-block:: python + + import psycopg2 + from opentelemetry.instrumentation.psycopg2 import Psycopg2Instrumentor + + # Alternatively, use instrument_connection for an individual connection + cnx = psycopg2.connect(database='Database') + instrumented_cnx = Psycopg2Instrumentor().instrument_connection(cnx) + cursor = instrumented_cnx.cursor() + cursor.execute("CREATE TABLE IF NOT EXISTS test (testField INTEGER)") + cursor.execute("INSERT INTO test (testField) VALUES (123)") + cursor.close() + instrumented_cnx.close() + API --- """ @@ -160,6 +176,19 @@ 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. + + 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 global TracerProvider will be used. + + Returns: + An instrumented psycopg2 connection object. + """ + if not hasattr(connection, "_is_instrumented_by_opentelemetry"): connection._is_instrumented_by_opentelemetry = False diff --git a/instrumentation/opentelemetry-instrumentation-pymysql/src/opentelemetry/instrumentation/pymysql/__init__.py b/instrumentation/opentelemetry-instrumentation-pymysql/src/opentelemetry/instrumentation/pymysql/__init__.py index eb4435813d..54c614f745 100644 --- a/instrumentation/opentelemetry-instrumentation-pymysql/src/opentelemetry/instrumentation/pymysql/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-pymysql/src/opentelemetry/instrumentation/pymysql/__init__.py @@ -26,6 +26,7 @@ import pymysql from opentelemetry.instrumentation.pymysql import PyMySQLInstrumentor + # Call instrument() to wrap all database connections PyMySQLInstrumentor().instrument() cnx = pymysql.connect(database="MySQL_Database") @@ -35,6 +36,28 @@ cursor.close() cnx.close() + +.. code:: python + + import pymysql + from opentelemetry.instrumentation.pymysql import PyMySQLInstrumentor + + # Alternatively, use instrument_connection for an individual connection + cnx = pymysql.connect(database="MySQL_Database") + instrumented_cnx = PyMySQLInstrumentor().instrument_connection( + cnx, + enable_commenter=True, + commenter_options={ + "db_driver": True, + "mysql_client_version": True + } + ) + cursor = instrumented_cnx.cursor() + cursor.execute("INSERT INTO test (testField) VALUES (123)" + instrumented_cnx.commit() + cursor.close() + instrumented_cnx.close() + SQLCOMMENTER ***************************************** You can optionally configure PyMySQL instrumentation to enable sqlcommenter which enriches @@ -165,10 +188,20 @@ def instrument_connection( """Enable instrumentation in a PyMySQL connection. Args: - connection: The connection to instrument. - tracer_provider: The optional tracer provider to use. If omitted - the current globally configured one is used. - + connection: + The existing PyMySQL connection instance that needs to be instrumented. + This connection was typically created using `pymysql.connect()` and is wrapped with OpenTelemetry tracing. + tracer_provider: + An optional `TracerProvider` instance that specifies which tracer provider should be used. + If not provided, the globally configured OpenTelemetry tracer provider is automatically applied. + enable_commenter: + A flag to enable the SQL Commenter feature. If `True`, query logs will be enriched with additional + contextual metadata (e.g., database version, traceparent IDs, driver information). + commenter_options: + A dictionary containing configuration options for the SQL Commenter feature. + You can specify various options, such as enabling driver information, database version logging, + traceparent propagation, and other customizable metadata enhancements. + See *SQLCommenter Configurations* above for more information. Returns: An instrumented connection. """ diff --git a/instrumentation/opentelemetry-instrumentation-sqlite3/src/opentelemetry/instrumentation/sqlite3/__init__.py b/instrumentation/opentelemetry-instrumentation-sqlite3/src/opentelemetry/instrumentation/sqlite3/__init__.py index ec4f8ecc50..086d47f3f5 100644 --- a/instrumentation/opentelemetry-instrumentation-sqlite3/src/opentelemetry/instrumentation/sqlite3/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-sqlite3/src/opentelemetry/instrumentation/sqlite3/__init__.py @@ -27,16 +27,31 @@ import sqlite3 from opentelemetry.instrumentation.sqlite3 import SQLite3Instrumentor - + # Call instrument() to wrap all database connections SQLite3Instrumentor().instrument() cnx = sqlite3.connect(':memory:') cursor = cnx.cursor() - cursor.execute("CREATE TABLE test (testField INTEGER)") + cursor.execute("CREATE TABLE IF NOT EXISTS test (testField INTEGER)") cursor.execute("INSERT INTO test (testField) VALUES (123)") cursor.close() cnx.close() +.. code:: python + + import sqlite3 + from opentelemetry.instrumentation.sqlite3 import SQLite3Instrumentor + + # Alternatively, use instrument_connection for an individual connection + conn = sqlite3.connect(":memory:") + instrumented_connection = SQLite3Instrumentor().instrument_connection(conn) + cursor = instrumented_connection.cursor() + cursor.execute("CREATE TABLE IF NOT EXISTS test (testField INTEGER)") + cursor.execute("INSERT INTO test (testField) VALUES (123)") + cursor.execute("SELECT * FROM test") + cursor.close() + instrumented_connection.close() + API --- """ @@ -104,9 +119,10 @@ def instrument_connection( the current globally configured one is used. Returns: - An instrumented connection. - """ + An instrumented SQLite connection that supports + telemetry for tracing database operations. + """ return dbapi.instrument_connection( __name__, connection, diff --git a/instrumentation/opentelemetry-instrumentation-sqlite3/tests/test_sqlite3.py b/instrumentation/opentelemetry-instrumentation-sqlite3/tests/test_sqlite3.py index 581920232b..b9fb1f10ec 100644 --- a/instrumentation/opentelemetry-instrumentation-sqlite3/tests/test_sqlite3.py +++ b/instrumentation/opentelemetry-instrumentation-sqlite3/tests/test_sqlite3.py @@ -29,6 +29,10 @@ def setUp(self): self._cursor = self._connection.cursor() self._connection2 = dbapi2.connect(":memory:") self._cursor2 = self._connection2.cursor() + self._connection3 = SQLite3Instrumentor.instrument_connection( + dbapi2.connect(":memory:") + ) + self._cursor3 = self._connection3.cursor() def tearDown(self): super().tearDown() @@ -40,6 +44,10 @@ def tearDown(self): self._cursor2.close() if self._connection2: self._connection2.close() + if self._cursor3: + self._cursor3.close() + if self._connection3: + self._connection3.close() SQLite3Instrumentor().uninstrument() def validate_spans(self, span_name): @@ -65,6 +73,7 @@ def _create_tables(self): stmt = "CREATE TABLE IF NOT EXISTS test (id integer)" self._cursor.execute(stmt) self._cursor2.execute(stmt) + self._cursor3.execute(stmt) self.memory_exporter.clear() def test_execute(self): @@ -78,6 +87,10 @@ def test_execute(self): self._cursor2.execute(stmt) self.validate_spans("CREATE") + with self._tracer.start_as_current_span("rootSpan"): + self._cursor3.execute(stmt) + self.validate_spans("CREATE") + def test_executemany(self): """Should create a child span for executemany""" self._create_tables() @@ -93,6 +106,10 @@ def test_executemany(self): self._cursor2.executemany(stmt, data) self.validate_spans("INSERT") + with self._tracer.start_as_current_span("rootSpan"): + self._cursor3.executemany(stmt, data) + self.validate_spans("INSERT") + def test_callproc(self): """Should create a child span for callproc""" with self._tracer.start_as_current_span("rootSpan"), self.assertRaises(