wrapture-instrumentation-mysql 1.0.0.dev1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- wrapture_instrumentation_mysql-1.0.0.dev1/CHANGES.md +9 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/Dockerfile +20 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/LICENSE +24 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/MANIFEST.in +8 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/PKG-INFO +181 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/README.md +158 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/TESTING.md +310 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/compose.yml +82 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/demo/__init__.py +0 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/demo/aiomysql.py +169 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/demo/mysqldb.py +156 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/demo/pymysql.py +157 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/pyproject.toml +134 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/setup.cfg +4 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/src/wrapture_instrumentation_mysql/__init__.py +26 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/src/wrapture_instrumentation_mysql/aiomysql/README.md +174 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/src/wrapture_instrumentation_mysql/aiomysql/__init__.py +58 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/src/wrapture_instrumentation_mysql/aiomysql/connection.py +113 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/src/wrapture_instrumentation_mysql/aiomysql/cursor.py +99 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/src/wrapture_instrumentation_mysql/common.py +151 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/src/wrapture_instrumentation_mysql/mysqldb/README.md +179 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/src/wrapture_instrumentation_mysql/mysqldb/__init__.py +98 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/src/wrapture_instrumentation_mysql/mysqldb/connection.py +161 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/src/wrapture_instrumentation_mysql/mysqldb/cursor.py +110 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/src/wrapture_instrumentation_mysql/py.typed +0 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/src/wrapture_instrumentation_mysql/pymysql/README.md +166 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/src/wrapture_instrumentation_mysql/pymysql/__init__.py +54 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/src/wrapture_instrumentation_mysql/pymysql/connection.py +113 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/src/wrapture_instrumentation_mysql/pymysql/cursor.py +97 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/src/wrapture_instrumentation_mysql.egg-info/PKG-INFO +181 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/src/wrapture_instrumentation_mysql.egg-info/SOURCES.txt +58 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/src/wrapture_instrumentation_mysql.egg-info/dependency_links.txt +1 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/src/wrapture_instrumentation_mysql.egg-info/entry_points.txt +4 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/src/wrapture_instrumentation_mysql.egg-info/not-zip-safe +1 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/src/wrapture_instrumentation_mysql.egg-info/requires.txt +1 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/src/wrapture_instrumentation_mysql.egg-info/top_level.txt +1 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/tests/__init__.py +0 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/tests/aiomysql/__init__.py +0 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/tests/aiomysql/conftest.py +35 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/tests/aiomysql/test_apply_remove.py +117 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/tests/aiomysql/test_class.py +70 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/tests/aiomysql/test_composition.py +151 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/tests/aiomysql/test_recording.py +536 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/tests/aiomysql/test_registration.py +85 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/tests/conftest.py +333 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/tests/mysqldb/__init__.py +0 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/tests/mysqldb/conftest.py +23 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/tests/mysqldb/test_apply_remove.py +136 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/tests/mysqldb/test_class.py +74 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/tests/mysqldb/test_composition.py +156 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/tests/mysqldb/test_recording.py +533 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/tests/mysqldb/test_registration.py +82 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/tests/pymysql/__init__.py +0 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/tests/pymysql/conftest.py +22 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/tests/pymysql/test_apply_remove.py +106 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/tests/pymysql/test_class.py +70 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/tests/pymysql/test_composition.py +154 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/tests/pymysql/test_recording.py +495 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/tests/pymysql/test_registration.py +80 -0
- wrapture_instrumentation_mysql-1.0.0.dev1/tests/test_package.py +179 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Changes
|
|
2
|
+
|
|
3
|
+
## Version 1.0.0
|
|
4
|
+
|
|
5
|
+
Initial release of wrapture-instrumentation-mysql. Everything is new
|
|
6
|
+
in this version, so rather than listing changes, see the
|
|
7
|
+
[README](README.md) for what the package provides: the table of
|
|
8
|
+
covered targets there links to each target's own notes describing
|
|
9
|
+
what it records and its settings.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# The environment the test matrix runs in: Debian with uv, plus what
|
|
2
|
+
# mysqlclient needs to build from its source distribution, since it
|
|
3
|
+
# ships no Linux or macOS wheels: pkg-config, the MySQL client
|
|
4
|
+
# development package (Debian's default-libmysqlclient-dev, which is
|
|
5
|
+
# MariaDB Connector/C) and a C compiler. Nothing else is baked in: uv
|
|
6
|
+
# fetches whichever interpreter the run asks for and installs the
|
|
7
|
+
# test dependencies into a per-version environment, both on named
|
|
8
|
+
# volumes so they persist across runs (see compose.yml), so the build
|
|
9
|
+
# of mysqlclient happens once per version. The repository is
|
|
10
|
+
# bind-mounted at run time, so the image never goes stale as the code
|
|
11
|
+
# changes.
|
|
12
|
+
|
|
13
|
+
FROM ghcr.io/astral-sh/uv:debian-slim
|
|
14
|
+
|
|
15
|
+
RUN apt-get update \
|
|
16
|
+
&& apt-get install -y --no-install-recommends \
|
|
17
|
+
default-libmysqlclient-dev pkg-config gcc libc6-dev \
|
|
18
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
19
|
+
|
|
20
|
+
WORKDIR /work
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
Copyright (c) 2026, Graham Dumpleton
|
|
2
|
+
All rights reserved.
|
|
3
|
+
|
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
|
5
|
+
modification, are permitted provided that the following conditions are met:
|
|
6
|
+
|
|
7
|
+
* Redistributions of source code must retain the above copyright notice, this
|
|
8
|
+
list of conditions and the following disclaimer.
|
|
9
|
+
|
|
10
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
|
11
|
+
this list of conditions and the following disclaimer in the documentation
|
|
12
|
+
and/or other materials provided with the distribution.
|
|
13
|
+
|
|
14
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
15
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
16
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
17
|
+
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
18
|
+
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
19
|
+
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
20
|
+
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
21
|
+
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
22
|
+
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
23
|
+
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
24
|
+
POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# setuptools includes test_*.py files by default but not the helpers
|
|
2
|
+
# they import (conftest.py, the package __init__.py), so the tests
|
|
3
|
+
# directory is taken whole, with the repository's other notes and the
|
|
4
|
+
# docker files the test matrix runs under.
|
|
5
|
+
graft tests
|
|
6
|
+
graft demo
|
|
7
|
+
include CHANGES.md TESTING.md compose.yml Dockerfile
|
|
8
|
+
global-exclude __pycache__ *.py[cod]
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: wrapture-instrumentation-mysql
|
|
3
|
+
Version: 1.0.0.dev1
|
|
4
|
+
Summary: Instrumentation for the MySQL client libraries (PyMySQL, mysqlclient, aiomysql), applied through wrapture.
|
|
5
|
+
Author-email: Graham Dumpleton <Graham.Dumpleton@gmail.com>
|
|
6
|
+
License-Expression: BSD-2-Clause
|
|
7
|
+
Project-URL: Homepage, https://github.com/GrahamDumpleton/wrapture-instrumentation-mysql
|
|
8
|
+
Project-URL: Documentation, https://wrapture.readthedocs.io
|
|
9
|
+
Project-URL: Bug Tracker, https://github.com/GrahamDumpleton/wrapture-instrumentation-mysql/issues/
|
|
10
|
+
Keywords: wrapture,instrumentation,mysql,pymysql,mysqlclient,MySQLdb,aiomysql,tracing
|
|
11
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
17
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
18
|
+
Requires-Python: >=3.12
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Requires-Dist: wrapture>=1.0.0a21
|
|
22
|
+
Dynamic: license-file
|
|
23
|
+
|
|
24
|
+
# wrapture-instrumentation-mysql
|
|
25
|
+
|
|
26
|
+
Instrumentation for the MySQL client libraries, applied through
|
|
27
|
+
[wrapture](https://github.com/GrahamDumpleton/wrapture).
|
|
28
|
+
|
|
29
|
+
wrapture attaches bindings to arbitrary Python call sites without
|
|
30
|
+
modifying the code being observed, and its config layer can switch on
|
|
31
|
+
packaged instrumentation for a third-party package by name. This is
|
|
32
|
+
the MySQL package in that collection: one `wrapture.Instrumentation`
|
|
33
|
+
class per client library, so tracing every query, connection and
|
|
34
|
+
transaction your application sends to MySQL (or MariaDB, through the
|
|
35
|
+
same drivers) is one config entry and no code.
|
|
36
|
+
|
|
37
|
+
> **Status: alpha, ahead of 1.0.0.** Developed against wrapture's
|
|
38
|
+
> alpha series, with pre-releases published to
|
|
39
|
+
> [PyPI](https://pypi.org/project/wrapture-instrumentation-mysql/),
|
|
40
|
+
> and until 1.0.0 is final a plain `pip install
|
|
41
|
+
> wrapture-instrumentation-mysql` picks up the latest pre-release
|
|
42
|
+
> automatically, so there is no need to pin a specific version.
|
|
43
|
+
|
|
44
|
+
## Why a separate package
|
|
45
|
+
|
|
46
|
+
The core
|
|
47
|
+
[wrapture-instrumentation](https://github.com/GrahamDumpleton/wrapture-instrumentation)
|
|
48
|
+
package deliberately covers only the standard library and third-party
|
|
49
|
+
packages that can be exercised in-process, with no separate backend
|
|
50
|
+
product or service needed to test against. A MySQL driver is exactly
|
|
51
|
+
the kind of target the separate-package rule was drawn for: its tests
|
|
52
|
+
need a real server, so this package's suite runs one in a docker
|
|
53
|
+
container, and it carries the drivers as test dependencies (one of
|
|
54
|
+
them built from source against a MySQL client library) and its own
|
|
55
|
+
release cadence, so the core package's test matrix stays light. One
|
|
56
|
+
package covers every client library for the one backend.
|
|
57
|
+
|
|
58
|
+
## Installation
|
|
59
|
+
|
|
60
|
+
```console
|
|
61
|
+
$ pip install wrapture-instrumentation-mysql
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Installing it brings wrapture and nothing else. No driver is a
|
|
65
|
+
dependency: each instrumentation is inert until its driver is present,
|
|
66
|
+
and wrapture checks the installed version against the range the
|
|
67
|
+
instrumentation supports at apply time.
|
|
68
|
+
|
|
69
|
+
## Using it
|
|
70
|
+
|
|
71
|
+
An `[[instrument]]` entry in `wrapture.toml` names the target:
|
|
72
|
+
|
|
73
|
+
```toml
|
|
74
|
+
[[instrument]]
|
|
75
|
+
name = "pymysql"
|
|
76
|
+
|
|
77
|
+
[[sink]]
|
|
78
|
+
type = "printer"
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
and the runner applies it before the application starts, so the patch
|
|
82
|
+
is in place before the driver is imported:
|
|
83
|
+
|
|
84
|
+
```console
|
|
85
|
+
$ python -m wrapture -m myapp
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
The same config works through
|
|
89
|
+
[autowrapt](https://github.com/GrahamDumpleton/autowrapt) injection
|
|
90
|
+
(`AUTOWRAPT_BOOTSTRAP=wrapture python myapp.py`); through
|
|
91
|
+
[manual setup](https://wrapture.readthedocs.io/en/latest/manual-setup.html),
|
|
92
|
+
a few lines in the application's own startup where wrapping the launch
|
|
93
|
+
from outside is awkward; and, in a test, through
|
|
94
|
+
`wrapture.instrumentation("pymysql")` scoping the instrumentation to
|
|
95
|
+
a block. The
|
|
96
|
+
[ad-hoc tracing guide](https://wrapture.readthedocs.io/en/latest/ad-hoc-tracing.html)
|
|
97
|
+
covers the config file itself.
|
|
98
|
+
|
|
99
|
+
To see what is installed, what it supports in the current
|
|
100
|
+
environment, and what settings it takes:
|
|
101
|
+
|
|
102
|
+
```console
|
|
103
|
+
$ python -m wrapture.tools instrumentation --verbose
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Provided instrumentation
|
|
107
|
+
|
|
108
|
+
| Target | Supported versions | Records | Settings |
|
|
109
|
+
| ------ | ------------------ | ------- | -------- |
|
|
110
|
+
| [`pymysql`](https://github.com/GrahamDumpleton/wrapture-instrumentation-mysql/blob/develop/src/wrapture_instrumentation_mysql/pymysql/README.md) | PyMySQL 1.1.1+ (1.x) | Every query as one `database` leaf, however it was issued (a cursor's `execute`, `executemany` or `callproc`, through whichever cursor class the application chose), plus the connection being opened and each transaction boundary the connection performs itself (`begin`, `commit`, `rollback`). Each event carries the system, the operation, and the database, host and port it reached; a failing statement records the driver's exception. The SQL text (the template with its placeholders) is recorded only with the `statement` setting on, bound parameters never. | `statement` |
|
|
111
|
+
| [`MySQLdb`](https://github.com/GrahamDumpleton/wrapture-instrumentation-mysql/blob/develop/src/wrapture_instrumentation_mysql/mysqldb/README.md) | mysqlclient 2.2.1+ (2.x), imported as `MySQLdb` | The same shapes through mysqlclient: every query as one `database` leaf (`execute`, `executemany`, `callproc`, through every cursor class), the connection being opened, and `begin`, `commit` and `rollback`, the last two bound over the C core's own methods. Each event carries the system, the operation, and the host and port it reached, plus the database from mysqlclient 2.2.7 on (earlier versions do not keep it); a failing statement records the driver's exception. The SQL text (the template with its placeholders) is recorded only with the `statement` setting on, bound parameters never. | `statement` |
|
|
112
|
+
| [`aiomysql`](https://github.com/GrahamDumpleton/wrapture-instrumentation-mysql/blob/develop/src/wrapture_instrumentation_mysql/aiomysql/README.md) | aiomysql 0.2+ (0.x) | The same shapes through aiomysql, each event recorded around its await: every query as one `database` leaf (`execute`, `executemany`, `callproc`, through every cursor class), the connection being opened (from a pool too), and `begin`, `commit` and `rollback`. Each event carries the system, the operation, and the database, host and port it reached; a failing statement records the driver's exception. The SQL text (the template with its placeholders) is recorded only with the `statement` setting on, bound parameters never. | `statement` |
|
|
113
|
+
|
|
114
|
+
The entry point name is the config's `name`, and is the import name
|
|
115
|
+
of the package the instrumentation patches (so mysqlclient's is
|
|
116
|
+
`MySQLdb`); the linked per-target README is the full user
|
|
117
|
+
documentation: what records, what the events carry, the setting, and
|
|
118
|
+
what is deliberately not traced.
|
|
119
|
+
|
|
120
|
+
## What is not traced
|
|
121
|
+
|
|
122
|
+
By design, and where it goes:
|
|
123
|
+
|
|
124
|
+
- Fetching rows: a query event closes when its execute returns, so
|
|
125
|
+
time spent iterating rows afterwards is the application's. An
|
|
126
|
+
unbuffered cursor (`SSCursor`) therefore records the send and the
|
|
127
|
+
wait for the first packet, not the streaming of the rows.
|
|
128
|
+
|
|
129
|
+
- The connection's context manager: in these drivers leaving a
|
|
130
|
+
`with connection:` block closes the connection and performs no
|
|
131
|
+
commit or rollback, so it is not a transaction boundary and records
|
|
132
|
+
nothing. The explicit `commit()` and `rollback()` calls do.
|
|
133
|
+
|
|
134
|
+
- Mode changes and housekeeping (`autocommit()`, `select_db()`,
|
|
135
|
+
`ping()`, `set_character_set()`, `show_warnings()`): not database
|
|
136
|
+
operations in the sense the events record.
|
|
137
|
+
|
|
138
|
+
- Pool bookkeeping (aiomysql): taking a connection from a pool and
|
|
139
|
+
returning it are not database operations; the connections the pool
|
|
140
|
+
opens do record.
|
|
141
|
+
|
|
142
|
+
- `LOAD DATA LOCAL INFILE` records as a statement like any other; the
|
|
143
|
+
file's contents never do.
|
|
144
|
+
|
|
145
|
+
## Testing against a server
|
|
146
|
+
|
|
147
|
+
The test suite drives the real drivers against a real MySQL server.
|
|
148
|
+
`WRAPTURE_MYSQL_URL` in the environment names one
|
|
149
|
+
(`mysql://user:password@host:port/database`); without it the suite
|
|
150
|
+
runs a throwaway `mysql:8.4` container itself, which needs Docker
|
|
151
|
+
Desktop or another docker daemon. With neither it fails rather than
|
|
152
|
+
skips. The mysqlclient driver builds from source against a MySQL
|
|
153
|
+
client library, so its suite runs inside a docker container that
|
|
154
|
+
carries the toolchain (`just test-docker`), and a plain native run
|
|
155
|
+
skips that one suite visibly rather than asking for a client library
|
|
156
|
+
on the machine. [TESTING.md](TESTING.md) covers the details, including how
|
|
157
|
+
the suite copes with MySQL 8's `caching_sha2_password` authentication
|
|
158
|
+
(the readiness probe's first connection warms the server's credential
|
|
159
|
+
cache, after which every driver connects plainly; a real application
|
|
160
|
+
on a cold cache over a plain socket needs the driver's own RSA or TLS
|
|
161
|
+
option, which is the driver's concern, not the instrumentation's).
|
|
162
|
+
|
|
163
|
+
## Adding a target
|
|
164
|
+
|
|
165
|
+
Each client library is its own subpackage and entry point here. The
|
|
166
|
+
subpackage's `__init__.py` holds one `wrapture.Instrumentation`
|
|
167
|
+
subclass and imports only wrapture (and the package's own `common.py`,
|
|
168
|
+
which imports only wrapture too); everything that touches the driver
|
|
169
|
+
lives in sibling modules imported inside the hook. The class is
|
|
170
|
+
registered in `pyproject.toml` under
|
|
171
|
+
`[project.entry-points."wrapture.instrumentation"]`, and gets its own
|
|
172
|
+
test suite under `tests/<target>/` and a `README.md` linked from the
|
|
173
|
+
table above. The
|
|
174
|
+
[instrumentation packages](https://wrapture.readthedocs.io/en/latest/instrumentation-packages.html)
|
|
175
|
+
page of the wrapture documentation is the full contract; TESTING.md
|
|
176
|
+
here covers the tests and the server they run against.
|
|
177
|
+
|
|
178
|
+
## License
|
|
179
|
+
|
|
180
|
+
BSD 2-Clause. See
|
|
181
|
+
[LICENSE](https://github.com/GrahamDumpleton/wrapture-instrumentation-mysql/blob/develop/LICENSE).
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# wrapture-instrumentation-mysql
|
|
2
|
+
|
|
3
|
+
Instrumentation for the MySQL client libraries, applied through
|
|
4
|
+
[wrapture](https://github.com/GrahamDumpleton/wrapture).
|
|
5
|
+
|
|
6
|
+
wrapture attaches bindings to arbitrary Python call sites without
|
|
7
|
+
modifying the code being observed, and its config layer can switch on
|
|
8
|
+
packaged instrumentation for a third-party package by name. This is
|
|
9
|
+
the MySQL package in that collection: one `wrapture.Instrumentation`
|
|
10
|
+
class per client library, so tracing every query, connection and
|
|
11
|
+
transaction your application sends to MySQL (or MariaDB, through the
|
|
12
|
+
same drivers) is one config entry and no code.
|
|
13
|
+
|
|
14
|
+
> **Status: alpha, ahead of 1.0.0.** Developed against wrapture's
|
|
15
|
+
> alpha series, with pre-releases published to
|
|
16
|
+
> [PyPI](https://pypi.org/project/wrapture-instrumentation-mysql/),
|
|
17
|
+
> and until 1.0.0 is final a plain `pip install
|
|
18
|
+
> wrapture-instrumentation-mysql` picks up the latest pre-release
|
|
19
|
+
> automatically, so there is no need to pin a specific version.
|
|
20
|
+
|
|
21
|
+
## Why a separate package
|
|
22
|
+
|
|
23
|
+
The core
|
|
24
|
+
[wrapture-instrumentation](https://github.com/GrahamDumpleton/wrapture-instrumentation)
|
|
25
|
+
package deliberately covers only the standard library and third-party
|
|
26
|
+
packages that can be exercised in-process, with no separate backend
|
|
27
|
+
product or service needed to test against. A MySQL driver is exactly
|
|
28
|
+
the kind of target the separate-package rule was drawn for: its tests
|
|
29
|
+
need a real server, so this package's suite runs one in a docker
|
|
30
|
+
container, and it carries the drivers as test dependencies (one of
|
|
31
|
+
them built from source against a MySQL client library) and its own
|
|
32
|
+
release cadence, so the core package's test matrix stays light. One
|
|
33
|
+
package covers every client library for the one backend.
|
|
34
|
+
|
|
35
|
+
## Installation
|
|
36
|
+
|
|
37
|
+
```console
|
|
38
|
+
$ pip install wrapture-instrumentation-mysql
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Installing it brings wrapture and nothing else. No driver is a
|
|
42
|
+
dependency: each instrumentation is inert until its driver is present,
|
|
43
|
+
and wrapture checks the installed version against the range the
|
|
44
|
+
instrumentation supports at apply time.
|
|
45
|
+
|
|
46
|
+
## Using it
|
|
47
|
+
|
|
48
|
+
An `[[instrument]]` entry in `wrapture.toml` names the target:
|
|
49
|
+
|
|
50
|
+
```toml
|
|
51
|
+
[[instrument]]
|
|
52
|
+
name = "pymysql"
|
|
53
|
+
|
|
54
|
+
[[sink]]
|
|
55
|
+
type = "printer"
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
and the runner applies it before the application starts, so the patch
|
|
59
|
+
is in place before the driver is imported:
|
|
60
|
+
|
|
61
|
+
```console
|
|
62
|
+
$ python -m wrapture -m myapp
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
The same config works through
|
|
66
|
+
[autowrapt](https://github.com/GrahamDumpleton/autowrapt) injection
|
|
67
|
+
(`AUTOWRAPT_BOOTSTRAP=wrapture python myapp.py`); through
|
|
68
|
+
[manual setup](https://wrapture.readthedocs.io/en/latest/manual-setup.html),
|
|
69
|
+
a few lines in the application's own startup where wrapping the launch
|
|
70
|
+
from outside is awkward; and, in a test, through
|
|
71
|
+
`wrapture.instrumentation("pymysql")` scoping the instrumentation to
|
|
72
|
+
a block. The
|
|
73
|
+
[ad-hoc tracing guide](https://wrapture.readthedocs.io/en/latest/ad-hoc-tracing.html)
|
|
74
|
+
covers the config file itself.
|
|
75
|
+
|
|
76
|
+
To see what is installed, what it supports in the current
|
|
77
|
+
environment, and what settings it takes:
|
|
78
|
+
|
|
79
|
+
```console
|
|
80
|
+
$ python -m wrapture.tools instrumentation --verbose
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Provided instrumentation
|
|
84
|
+
|
|
85
|
+
| Target | Supported versions | Records | Settings |
|
|
86
|
+
| ------ | ------------------ | ------- | -------- |
|
|
87
|
+
| [`pymysql`](https://github.com/GrahamDumpleton/wrapture-instrumentation-mysql/blob/develop/src/wrapture_instrumentation_mysql/pymysql/README.md) | PyMySQL 1.1.1+ (1.x) | Every query as one `database` leaf, however it was issued (a cursor's `execute`, `executemany` or `callproc`, through whichever cursor class the application chose), plus the connection being opened and each transaction boundary the connection performs itself (`begin`, `commit`, `rollback`). Each event carries the system, the operation, and the database, host and port it reached; a failing statement records the driver's exception. The SQL text (the template with its placeholders) is recorded only with the `statement` setting on, bound parameters never. | `statement` |
|
|
88
|
+
| [`MySQLdb`](https://github.com/GrahamDumpleton/wrapture-instrumentation-mysql/blob/develop/src/wrapture_instrumentation_mysql/mysqldb/README.md) | mysqlclient 2.2.1+ (2.x), imported as `MySQLdb` | The same shapes through mysqlclient: every query as one `database` leaf (`execute`, `executemany`, `callproc`, through every cursor class), the connection being opened, and `begin`, `commit` and `rollback`, the last two bound over the C core's own methods. Each event carries the system, the operation, and the host and port it reached, plus the database from mysqlclient 2.2.7 on (earlier versions do not keep it); a failing statement records the driver's exception. The SQL text (the template with its placeholders) is recorded only with the `statement` setting on, bound parameters never. | `statement` |
|
|
89
|
+
| [`aiomysql`](https://github.com/GrahamDumpleton/wrapture-instrumentation-mysql/blob/develop/src/wrapture_instrumentation_mysql/aiomysql/README.md) | aiomysql 0.2+ (0.x) | The same shapes through aiomysql, each event recorded around its await: every query as one `database` leaf (`execute`, `executemany`, `callproc`, through every cursor class), the connection being opened (from a pool too), and `begin`, `commit` and `rollback`. Each event carries the system, the operation, and the database, host and port it reached; a failing statement records the driver's exception. The SQL text (the template with its placeholders) is recorded only with the `statement` setting on, bound parameters never. | `statement` |
|
|
90
|
+
|
|
91
|
+
The entry point name is the config's `name`, and is the import name
|
|
92
|
+
of the package the instrumentation patches (so mysqlclient's is
|
|
93
|
+
`MySQLdb`); the linked per-target README is the full user
|
|
94
|
+
documentation: what records, what the events carry, the setting, and
|
|
95
|
+
what is deliberately not traced.
|
|
96
|
+
|
|
97
|
+
## What is not traced
|
|
98
|
+
|
|
99
|
+
By design, and where it goes:
|
|
100
|
+
|
|
101
|
+
- Fetching rows: a query event closes when its execute returns, so
|
|
102
|
+
time spent iterating rows afterwards is the application's. An
|
|
103
|
+
unbuffered cursor (`SSCursor`) therefore records the send and the
|
|
104
|
+
wait for the first packet, not the streaming of the rows.
|
|
105
|
+
|
|
106
|
+
- The connection's context manager: in these drivers leaving a
|
|
107
|
+
`with connection:` block closes the connection and performs no
|
|
108
|
+
commit or rollback, so it is not a transaction boundary and records
|
|
109
|
+
nothing. The explicit `commit()` and `rollback()` calls do.
|
|
110
|
+
|
|
111
|
+
- Mode changes and housekeeping (`autocommit()`, `select_db()`,
|
|
112
|
+
`ping()`, `set_character_set()`, `show_warnings()`): not database
|
|
113
|
+
operations in the sense the events record.
|
|
114
|
+
|
|
115
|
+
- Pool bookkeeping (aiomysql): taking a connection from a pool and
|
|
116
|
+
returning it are not database operations; the connections the pool
|
|
117
|
+
opens do record.
|
|
118
|
+
|
|
119
|
+
- `LOAD DATA LOCAL INFILE` records as a statement like any other; the
|
|
120
|
+
file's contents never do.
|
|
121
|
+
|
|
122
|
+
## Testing against a server
|
|
123
|
+
|
|
124
|
+
The test suite drives the real drivers against a real MySQL server.
|
|
125
|
+
`WRAPTURE_MYSQL_URL` in the environment names one
|
|
126
|
+
(`mysql://user:password@host:port/database`); without it the suite
|
|
127
|
+
runs a throwaway `mysql:8.4` container itself, which needs Docker
|
|
128
|
+
Desktop or another docker daemon. With neither it fails rather than
|
|
129
|
+
skips. The mysqlclient driver builds from source against a MySQL
|
|
130
|
+
client library, so its suite runs inside a docker container that
|
|
131
|
+
carries the toolchain (`just test-docker`), and a plain native run
|
|
132
|
+
skips that one suite visibly rather than asking for a client library
|
|
133
|
+
on the machine. [TESTING.md](TESTING.md) covers the details, including how
|
|
134
|
+
the suite copes with MySQL 8's `caching_sha2_password` authentication
|
|
135
|
+
(the readiness probe's first connection warms the server's credential
|
|
136
|
+
cache, after which every driver connects plainly; a real application
|
|
137
|
+
on a cold cache over a plain socket needs the driver's own RSA or TLS
|
|
138
|
+
option, which is the driver's concern, not the instrumentation's).
|
|
139
|
+
|
|
140
|
+
## Adding a target
|
|
141
|
+
|
|
142
|
+
Each client library is its own subpackage and entry point here. The
|
|
143
|
+
subpackage's `__init__.py` holds one `wrapture.Instrumentation`
|
|
144
|
+
subclass and imports only wrapture (and the package's own `common.py`,
|
|
145
|
+
which imports only wrapture too); everything that touches the driver
|
|
146
|
+
lives in sibling modules imported inside the hook. The class is
|
|
147
|
+
registered in `pyproject.toml` under
|
|
148
|
+
`[project.entry-points."wrapture.instrumentation"]`, and gets its own
|
|
149
|
+
test suite under `tests/<target>/` and a `README.md` linked from the
|
|
150
|
+
table above. The
|
|
151
|
+
[instrumentation packages](https://wrapture.readthedocs.io/en/latest/instrumentation-packages.html)
|
|
152
|
+
page of the wrapture documentation is the full contract; TESTING.md
|
|
153
|
+
here covers the tests and the server they run against.
|
|
154
|
+
|
|
155
|
+
## License
|
|
156
|
+
|
|
157
|
+
BSD 2-Clause. See
|
|
158
|
+
[LICENSE](https://github.com/GrahamDumpleton/wrapture-instrumentation-mysql/blob/develop/LICENSE).
|