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

Add mysql-connector instrumentor support for sqlcommenting #3023

Closed
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
5e1e57a
DB-API integration: abstract classes BaseTracedConnection|CursorProxy
tammy-baylis-swi Nov 19, 2024
d242454
Add mysql-connector sqlcomment support
tammy-baylis-swi Nov 19, 2024
089c90a
Add tests
tammy-baylis-swi Nov 19, 2024
bb1f010
Changelog
tammy-baylis-swi Nov 19, 2024
b3c3aae
Merge branch 'main' into mysqlconnector-sqlcomment-v4
tammy-baylis-swi Nov 19, 2024
7fabfcf
Mv dbapi base class to proxy.py
tammy-baylis-swi Nov 19, 2024
83f8e67
Add docstring
tammy-baylis-swi Nov 19, 2024
569e811
Use class attr in Base to fix sqlite3 integration
tammy-baylis-swi Nov 19, 2024
5acb2d0
Add BaseMeta helper for pypy
tammy-baylis-swi Nov 19, 2024
4cbcfe7
Merge branch 'main' into mysqlconnector-sqlcomment-v4
tammy-baylis-swi Nov 19, 2024
78c5130
Merge branch 'main' into mysqlconnector-sqlcomment-v4
tammy-baylis-swi Nov 21, 2024
880c0f7
mysql instrument_connection passes connect_module
tammy-baylis-swi Nov 21, 2024
94bf04a
Update comment
tammy-baylis-swi Nov 26, 2024
19bfd7c
dbapi.instrument_connection optional kwargs db_api_integration_factor…
tammy-baylis-swi Nov 26, 2024
ee907e2
mysql uses updated dbapi.instrument_connection
tammy-baylis-swi Nov 26, 2024
4e1b778
Simplify get_traced_connection_proxy and update comment
tammy-baylis-swi Nov 26, 2024
a0d3736
Rm is_prepared helper, add quick kwargs check
tammy-baylis-swi Nov 26, 2024
957636a
Add dbapi proxy tests
tammy-baylis-swi Nov 28, 2024
11d4f07
Add dbapi instr_cnx optional params to tests
tammy-baylis-swi Nov 28, 2024
1cf2238
Mv test
tammy-baylis-swi Nov 28, 2024
cb9a475
Add mysql sqlcomment integration tests
tammy-baylis-swi Nov 28, 2024
b9a52e9
Add get_traced_cnx or cur_proxy tests
tammy-baylis-swi Nov 28, 2024
762eea9
Merge branch 'main' into mysqlconnector-sqlcomment-v4
tammy-baylis-swi Nov 28, 2024
da13a71
Update doc
tammy-baylis-swi Nov 28, 2024
cc8ef8e
Merge branch 'main' into mysqlconnector-sqlcomment-v4
tammy-baylis-swi Dec 2, 2024
b39fd0c
Merge branch 'main' into mysqlconnector-sqlcomment-v4
tammy-baylis-swi Dec 11, 2024
b62bd54
Merge branch 'main' into mysqlconnector-sqlcomment-v4
tammy-baylis-swi Dec 19, 2024
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 @@ -25,6 +25,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
([#3027](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3027))
- `opentelemetry-instrumentation-mysqlclient` Add sqlcommenter support
([#2941](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2941))
- `opentelemetry-instrumentation-mysql` Add sqlcommenter support
([#3023](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3023))

### Fixed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@
import wrapt

from opentelemetry import trace as trace_api
from opentelemetry.instrumentation.dbapi.proxy import (
BaseTracedConnectionProxy,
BaseTracedCursorProxy,
)
from opentelemetry.instrumentation.dbapi.version import __version__
from opentelemetry.instrumentation.sqlcommenter_utils import _add_sql_comment
from opentelemetry.instrumentation.utils import (
Expand Down Expand Up @@ -191,6 +195,8 @@ def instrument_connection(
enable_commenter: bool = False,
commenter_options: dict = None,
connect_module: typing.Callable[..., typing.Any] = None,
db_api_integration_factory: typing.ClassVar = None,
get_cnx_proxy: typing.Callable[..., typing.Any] = None,
):
"""Enable instrumentation in a database connection.

Expand All @@ -206,6 +212,10 @@ def instrument_connection(
enable_commenter: Flag to enable/disable sqlcommenter.
commenter_options: Configurations for tags to be appended at the sql query.
connect_module: Module name where connect method is available.
db_api_integration_factory: The `DatabaseApiIntegration` to use. If none is passed the
default one is used.
get_cnx_proxy: Method to get traced connextion proxy. If none is passed the
default one is used.
Comment on lines +215 to +218
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding these optional kwargs to reduce code duplication. mysql instrumentor doesn't need to implement its own instrument_connection but can still customize TracedConnectionProxy.cursor to check user-specified cursor params. Open to other ideas too!


Returns:
An instrumented connection.
Expand All @@ -214,7 +224,11 @@ def instrument_connection(
_logger.warning("Connection already instrumented")
return connection

db_integration = DatabaseApiIntegration(
db_api_integration_factory = (
db_api_integration_factory or DatabaseApiIntegration
)

db_integration = db_api_integration_factory(
name,
database_system,
connection_attributes=connection_attributes,
Expand All @@ -226,7 +240,9 @@ def instrument_connection(
connect_module=connect_module,
)
db_integration.get_connection_attributes(connection)
return get_traced_connection_proxy(connection, db_integration)

get_cnx_proxy = get_cnx_proxy or get_traced_connection_proxy
return get_cnx_proxy(connection, db_integration)


def uninstrument_connection(connection):
Expand Down Expand Up @@ -397,31 +413,12 @@ def get_traced_connection_proxy(
connection, db_api_integration, *args, **kwargs
):
# pylint: disable=abstract-method
class TracedConnectionProxy(wrapt.ObjectProxy):
# pylint: disable=unused-argument
def __init__(self, connection, *args, **kwargs):
wrapt.ObjectProxy.__init__(self, connection)

def __getattribute__(self, name):
if object.__getattribute__(self, name):
return object.__getattribute__(self, name)

return object.__getattribute__(
object.__getattribute__(self, "_connection"), name
)

class TracedConnectionProxy(BaseTracedConnectionProxy):
def cursor(self, *args, **kwargs):
return get_traced_cursor_proxy(
self.__wrapped__.cursor(*args, **kwargs), db_api_integration
)

def __enter__(self):
self.__wrapped__.__enter__()
return self

def __exit__(self, *args, **kwargs):
self.__wrapped__.__exit__(*args, **kwargs)

return TracedConnectionProxy(connection, *args, **kwargs)


Expand Down Expand Up @@ -547,34 +544,11 @@ def traced_execution(


def get_traced_cursor_proxy(cursor, db_api_integration, *args, **kwargs):
_cursor_tracer = CursorTracer(db_api_integration)

# pylint: disable=abstract-method
class TracedCursorProxy(wrapt.ObjectProxy):
# pylint: disable=unused-argument
def __init__(self, cursor, *args, **kwargs):
wrapt.ObjectProxy.__init__(self, cursor)

def execute(self, *args, **kwargs):
return _cursor_tracer.traced_execution(
self.__wrapped__, self.__wrapped__.execute, *args, **kwargs
class TracedCursorProxy(BaseTracedCursorProxy):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._cursor_tracer = CursorTracer(
db_api_integration,
)

def executemany(self, *args, **kwargs):
return _cursor_tracer.traced_execution(
self.__wrapped__, self.__wrapped__.executemany, *args, **kwargs
)

def callproc(self, *args, **kwargs):
return _cursor_tracer.traced_execution(
self.__wrapped__, self.__wrapped__.callproc, *args, **kwargs
)

def __enter__(self):
self.__wrapped__.__enter__()
return self

def __exit__(self, *args, **kwargs):
self.__wrapped__.__exit__(*args, **kwargs)

return TracedCursorProxy(cursor, *args, **kwargs)
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# Copyright The OpenTelemetry Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


from abc import ABC, ABCMeta, abstractmethod

import wrapt


class BaseMeta(ABCMeta, type(wrapt.ObjectProxy)):
"""Metaclass compatibility helper for PyPy for derived classes"""


class BaseTracedConnectionProxy(ABC, wrapt.ObjectProxy, metaclass=BaseMeta):
"""ABC for traced database client connection proxy.

Child classes are used to wrap Connection objects and
generate telemetry based on provided integration settings.

Child classes must implement cursor(), which should return
a traced database client cursor proxy depending on database
driver needs.
"""

# pylint: disable=unused-argument
def __init__(self, connection, *args, **kwargs):
wrapt.ObjectProxy.__init__(self, connection)

def __getattribute__(self, name):
if object.__getattribute__(self, name):
return object.__getattribute__(self, name)

return object.__getattribute__(
object.__getattribute__(self, "_connection"), name
)

@abstractmethod
def cursor(self, *args, **kwargs):
"""Returns instrumented database query cursor"""

def __enter__(self):
self.__wrapped__.__enter__()
return self

def __exit__(self, *args, **kwargs):
self.__wrapped__.__exit__(*args, **kwargs)


# pylint: disable=abstract-method
class BaseTracedCursorProxy(ABC, wrapt.ObjectProxy, metaclass=BaseMeta):
"""ABC for traced database client cursor proxy.

Child classes are used to wrap Cursor objects and
generate telemetry based on provided integration settings.

Child classes must implement __init__(), which should set
class variable _cursor_tracer as a CursorTracer depending
on database driver needs.
"""

_cursor_tracer = None

# pylint: disable=unused-argument
@abstractmethod
def __init__(self, cursor, *args, **kwargs):
"""Wrap db client cursor for tracing"""
wrapt.ObjectProxy.__init__(self, cursor)

def callproc(self, *args, **kwargs):
return self._cursor_tracer.traced_execution(
self.__wrapped__, self.__wrapped__.callproc, *args, **kwargs
)

def execute(self, *args, **kwargs):
return self._cursor_tracer.traced_execution(
self.__wrapped__, self.__wrapped__.execute, *args, **kwargs
)

def executemany(self, *args, **kwargs):
return self._cursor_tracer.traced_execution(
self.__wrapped__, self.__wrapped__.executemany, *args, **kwargs
)

def __enter__(self):
self.__wrapped__.__enter__()
return self

def __exit__(self, *args, **kwargs):
self.__wrapped__.__exit__(*args, **kwargs)
Loading