mirror of
https://github.com/open-telemetry/opentelemetry-python-contrib.git
synced 2026-03-13 08:10:39 +08:00
fix instrument of typed psycopg sql (#4171)
* fix instrument of typed psycopg sql The instrumentation is not working when using [typed `SQL`](https://www.psycopg.org/psycopg3/docs/api/sql.html) from psycopg (only when using the `Composed` type, that is returned when the query is formated). ```python from psycopg.sql import SQL query = SQL("SELECT * FROM test") ``` This fixes it by checking the `Composable` base class instead of the more restricted `Composed`. * add changelog * fix tests for python 3.9 using an identifier requires a real connection, I just replaced it since we only want to test with a composed. --------- Co-authored-by: Riccardo Magliocchetti <riccardo.magliocchetti@gmail.com>
This commit is contained in:
@@ -104,6 +104,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
([#3922](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3922))
|
||||
- `opentelemetry-instrumentation-urllib3`: fix multiple arguments error
|
||||
([#4144](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4144))
|
||||
- `opentelemetry-instrumentation-psycopg`: Fix instrument of typed psycopg sql
|
||||
([#4078](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4171))
|
||||
- `opentelemetry-instrumentation-aiohttp-server`: fix HTTP error inconsistencies
|
||||
([#4175](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4175))
|
||||
|
||||
@@ -112,7 +114,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- `opentelemetry-instrumentation-logging`: Inject span context attributes into logging LogRecord only if configured to do so
|
||||
([#4112](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4112))
|
||||
- `opentelemetry-instrumentation-django`: Drop support for Django < 2.0
|
||||
([#3848](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4083))
|
||||
([#4083](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4083))
|
||||
|
||||
## Version 1.39.0/0.60b0 (2025-12-03)
|
||||
|
||||
|
||||
@@ -147,7 +147,7 @@ import logging
|
||||
from typing import Any, Callable, Collection, TypeVar
|
||||
|
||||
import psycopg # pylint: disable=import-self
|
||||
from psycopg.sql import Composed # pylint: disable=no-name-in-module
|
||||
from psycopg.sql import Composable # pylint: disable=no-name-in-module
|
||||
|
||||
from opentelemetry.instrumentation import dbapi
|
||||
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
|
||||
@@ -338,7 +338,7 @@ class CursorTracer(dbapi.CursorTracer):
|
||||
return ""
|
||||
|
||||
statement = args[0]
|
||||
if isinstance(statement, Composed):
|
||||
if isinstance(statement, Composable):
|
||||
statement = statement.as_string(cursor)
|
||||
|
||||
# `statement` can be empty string. See #2643
|
||||
@@ -353,7 +353,7 @@ class CursorTracer(dbapi.CursorTracer):
|
||||
return ""
|
||||
|
||||
statement = args[0]
|
||||
if isinstance(statement, Composed):
|
||||
if isinstance(statement, Composable):
|
||||
statement = statement.as_string(cursor)
|
||||
return statement
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import types
|
||||
from unittest import IsolatedAsyncioTestCase, mock
|
||||
|
||||
import psycopg
|
||||
from psycopg.sql import SQL, Composed
|
||||
|
||||
import opentelemetry.instrumentation.psycopg
|
||||
from opentelemetry.instrumentation.psycopg import PsycopgInstrumentor
|
||||
@@ -34,6 +35,8 @@ class MockCursor:
|
||||
callproc = mock.MagicMock(spec=types.MethodType)
|
||||
callproc.__name__ = "callproc"
|
||||
|
||||
connection = None
|
||||
|
||||
rowcount = "SomeRowCount"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@@ -348,6 +351,41 @@ class TestPostgresqlIntegration(PostgresqlIntegrationTestMixin, TestBase):
|
||||
spans_list = self.memory_exporter.get_finished_spans()
|
||||
self.assertEqual(len(spans_list), 1)
|
||||
|
||||
def test_instrument_connection_typed_sql_query(self):
|
||||
cnx = psycopg.connect(database="test")
|
||||
query = SQL("SELECT * FROM test")
|
||||
|
||||
cnx = PsycopgInstrumentor().instrument_connection(cnx)
|
||||
|
||||
self.assertTrue(issubclass(cnx.cursor_factory, MockCursor))
|
||||
|
||||
cursor = cnx.cursor()
|
||||
cursor.execute(query)
|
||||
|
||||
spans_list = self.memory_exporter.get_finished_spans()
|
||||
self.assertEqual(len(spans_list), 1)
|
||||
self.assertEqual(spans_list[0].name, "SELECT")
|
||||
self.assertEqual(
|
||||
spans_list[0].attributes["db.statement"], "SELECT * FROM test"
|
||||
)
|
||||
|
||||
def test_instrument_connection_composed_query(self):
|
||||
cnx = psycopg.connect(database="test")
|
||||
query: Composed = SQL("SELECT * FROM test").format()
|
||||
|
||||
cnx = PsycopgInstrumentor().instrument_connection(cnx)
|
||||
self.assertTrue(issubclass(cnx.cursor_factory, MockCursor))
|
||||
|
||||
cursor = cnx.cursor()
|
||||
cursor.execute(query)
|
||||
|
||||
spans_list = self.memory_exporter.get_finished_spans()
|
||||
self.assertEqual(len(spans_list), 1)
|
||||
self.assertEqual(spans_list[0].name, "SELECT")
|
||||
self.assertEqual(
|
||||
spans_list[0].attributes["db.statement"], "SELECT * FROM test"
|
||||
)
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def test_instrument_connection_with_instrument(self):
|
||||
cnx = psycopg.connect(database="test")
|
||||
|
||||
Reference in New Issue
Block a user