queryargus 0.1.0__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.
- queryargus-0.1.0/LICENSE +21 -0
- queryargus-0.1.0/PKG-INFO +368 -0
- queryargus-0.1.0/README.md +304 -0
- queryargus-0.1.0/pyproject.toml +95 -0
- queryargus-0.1.0/src/queryargus/__init__.py +36 -0
- queryargus-0.1.0/src/queryargus/_internal/__init__.py +1 -0
- queryargus-0.1.0/src/queryargus/_internal/clock.py +18 -0
- queryargus-0.1.0/src/queryargus/_internal/collector.py +40 -0
- queryargus-0.1.0/src/queryargus/_internal/context.py +43 -0
- queryargus-0.1.0/src/queryargus/_internal/detection.py +100 -0
- queryargus-0.1.0/src/queryargus/_internal/events.py +53 -0
- queryargus-0.1.0/src/queryargus/_internal/fingerprint.py +20 -0
- queryargus-0.1.0/src/queryargus/_internal/ids.py +11 -0
- queryargus-0.1.0/src/queryargus/_internal/inference.py +132 -0
- queryargus-0.1.0/src/queryargus/_internal/registry.py +45 -0
- queryargus-0.1.0/src/queryargus/_internal/serialization.py +86 -0
- queryargus-0.1.0/src/queryargus/adapters/__init__.py +13 -0
- queryargus-0.1.0/src/queryargus/adapters/asyncpg.py +74 -0
- queryargus-0.1.0/src/queryargus/adapters/base.py +16 -0
- queryargus-0.1.0/src/queryargus/adapters/psycopg.py +97 -0
- queryargus-0.1.0/src/queryargus/adapters/sqlalchemy.py +78 -0
- queryargus-0.1.0/src/queryargus/analysis/__init__.py +16 -0
- queryargus-0.1.0/src/queryargus/analysis/coverage.py +30 -0
- queryargus-0.1.0/src/queryargus/analysis/heuristics.py +329 -0
- queryargus-0.1.0/src/queryargus/analysis/metrics.py +17 -0
- queryargus-0.1.0/src/queryargus/analysis/report.py +27 -0
- queryargus-0.1.0/src/queryargus/cli/__init__.py +1 -0
- queryargus-0.1.0/src/queryargus/cli/_charts.py +233 -0
- queryargus-0.1.0/src/queryargus/cli/_styles.py +200 -0
- queryargus-0.1.0/src/queryargus/cli/badge.py +86 -0
- queryargus-0.1.0/src/queryargus/cli/report.py +834 -0
- queryargus-0.1.0/src/queryargus/cli/summary.py +73 -0
- queryargus-0.1.0/src/queryargus/config.py +16 -0
- queryargus-0.1.0/src/queryargus/decorators.py +98 -0
- queryargus-0.1.0/src/queryargus/exceptions.py +9 -0
- queryargus-0.1.0/src/queryargus/instrumentation.py +90 -0
- queryargus-0.1.0/src/queryargus/integrations/__init__.py +6 -0
- queryargus-0.1.0/src/queryargus/integrations/fastapi.py +52 -0
- queryargus-0.1.0/src/queryargus/integrations/starlette.py +52 -0
- queryargus-0.1.0/src/queryargus/models.py +123 -0
- queryargus-0.1.0/src/queryargus/py.typed +1 -0
- queryargus-0.1.0/src/queryargus/scope.py +49 -0
- queryargus-0.1.0/src/queryargus/setup.py +156 -0
- queryargus-0.1.0/src/queryargus/sinks/__init__.py +7 -0
- queryargus-0.1.0/src/queryargus/sinks/base.py +15 -0
- queryargus-0.1.0/src/queryargus/sinks/jsonl.py +24 -0
- queryargus-0.1.0/src/queryargus/sinks/memory.py +16 -0
- queryargus-0.1.0/src/queryargus/testing/__init__.py +20 -0
- queryargus-0.1.0/src/queryargus/testing/assertions.py +73 -0
- queryargus-0.1.0/src/queryargus/testing/capture.py +66 -0
- queryargus-0.1.0/src/queryargus/testing/plugin.py +33 -0
- queryargus-0.1.0/src/queryargus/trace.py +108 -0
queryargus-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 matheuss0xf
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: queryargus
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Scope-aware SQL query tracing for Python applications, tests, and offline reports.
|
|
5
|
+
Keywords: sql,query tracing,observability,performance,fastapi,sqlalchemy,pytest,n+1
|
|
6
|
+
Author: matheuss0xf
|
|
7
|
+
License: MIT License
|
|
8
|
+
|
|
9
|
+
Copyright (c) 2026 matheuss0xf
|
|
10
|
+
|
|
11
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
12
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
13
|
+
in the Software without restriction, including without limitation the rights
|
|
14
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
15
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
16
|
+
furnished to do so, subject to the following conditions:
|
|
17
|
+
|
|
18
|
+
The above copyright notice and this permission notice shall be included in all
|
|
19
|
+
copies or substantial portions of the Software.
|
|
20
|
+
|
|
21
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
22
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
23
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
24
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
25
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
26
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
27
|
+
SOFTWARE.
|
|
28
|
+
Classifier: Development Status :: 3 - Alpha
|
|
29
|
+
Classifier: Intended Audience :: Developers
|
|
30
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
31
|
+
Classifier: Operating System :: OS Independent
|
|
32
|
+
Classifier: Programming Language :: Python
|
|
33
|
+
Classifier: Programming Language :: Python :: 3
|
|
34
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
35
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
36
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
37
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
38
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
39
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
40
|
+
Classifier: Framework :: AsyncIO
|
|
41
|
+
Classifier: Framework :: Pytest
|
|
42
|
+
Classifier: Topic :: Database
|
|
43
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
44
|
+
Classifier: Topic :: Software Development :: Testing
|
|
45
|
+
Classifier: Typing :: Typed
|
|
46
|
+
Requires-Dist: asyncpg>=0.29,<1 ; extra == 'asyncpg'
|
|
47
|
+
Requires-Dist: fastapi>=0.115,<1 ; extra == 'fastapi'
|
|
48
|
+
Requires-Dist: starlette>=0.37,<1 ; extra == 'fastapi'
|
|
49
|
+
Requires-Dist: psycopg>=3,<4 ; extra == 'psycopg'
|
|
50
|
+
Requires-Dist: sqlalchemy>=2,<3 ; extra == 'sqlalchemy'
|
|
51
|
+
Requires-Dist: pytest>=8,<9 ; extra == 'testing'
|
|
52
|
+
Requires-Python: >=3.10, <3.15
|
|
53
|
+
Project-URL: Homepage, https://github.com/matheuss0xf/queryargus
|
|
54
|
+
Project-URL: Repository, https://github.com/matheuss0xf/queryargus
|
|
55
|
+
Project-URL: Documentation, https://github.com/matheuss0xf/queryargus/tree/main/docs
|
|
56
|
+
Project-URL: Changelog, https://github.com/matheuss0xf/queryargus/blob/main/CHANGELOG.md
|
|
57
|
+
Project-URL: Issues, https://github.com/matheuss0xf/queryargus/issues
|
|
58
|
+
Provides-Extra: asyncpg
|
|
59
|
+
Provides-Extra: fastapi
|
|
60
|
+
Provides-Extra: psycopg
|
|
61
|
+
Provides-Extra: sqlalchemy
|
|
62
|
+
Provides-Extra: testing
|
|
63
|
+
Description-Content-Type: text/markdown
|
|
64
|
+
|
|
65
|
+
# QueryArgus
|
|
66
|
+
|
|
67
|
+
See every query. Miss nothing.
|
|
68
|
+
|
|
69
|
+
QueryArgus is a Python library for tracing SQL queries across requests, jobs, tests, CLI commands, and regular functions. It captures query execution with business context, stores finished traces in simple offline-friendly formats, and helps you understand repeated queries, hotspots, and possible N+1 sequences without forcing an APM-shaped workflow on your application.
|
|
70
|
+
|
|
71
|
+
[Portuguese (Brazil) documentation](https://github.com/matheuss0xf/queryargus/blob/main/README.pt-BR.md)
|
|
72
|
+
|
|
73
|
+
## Why QueryArgus
|
|
74
|
+
|
|
75
|
+
Database behavior often becomes opaque as an application grows:
|
|
76
|
+
|
|
77
|
+
- N+1 issues slip into production unnoticed.
|
|
78
|
+
- Performance regressions show up late.
|
|
79
|
+
- Generic APMs reveal symptoms, but not the actual query flow inside the unit of work you care about.
|
|
80
|
+
- Plain logs rarely connect a query to the request, job, or repository method that caused it.
|
|
81
|
+
|
|
82
|
+
QueryArgus exists to make query behavior visible, understandable, and actionable.
|
|
83
|
+
|
|
84
|
+
## What it does
|
|
85
|
+
|
|
86
|
+
- Captures SQL queries through pluggable adapters.
|
|
87
|
+
- Associates queries with a root trace and nested business scopes.
|
|
88
|
+
- Infers a useful scope from caller code when you do not set one explicitly.
|
|
89
|
+
- Works in HTTP apps, background jobs, worker handlers, tests, and CLI code.
|
|
90
|
+
- Persists finished traces to JSONL or memory sinks.
|
|
91
|
+
- Generates offline reports in text, JSON, HTML, SVG badge, and Markdown summary formats.
|
|
92
|
+
- Provides pytest fixtures and assertions for query-aware tests.
|
|
93
|
+
|
|
94
|
+
## Installation
|
|
95
|
+
|
|
96
|
+
Install the core package:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
pip install queryargus
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Install common integrations:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
pip install "queryargus[sqlalchemy,fastapi,testing]"
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Available extras:
|
|
109
|
+
|
|
110
|
+
- `sqlalchemy`: SQLAlchemy `Engine` and `AsyncEngine` instrumentation.
|
|
111
|
+
- `psycopg`: psycopg 3 sync and async connection instrumentation.
|
|
112
|
+
- `asyncpg`: asyncpg connection instrumentation.
|
|
113
|
+
- `fastapi`: FastAPI and Starlette middleware integration.
|
|
114
|
+
- `testing`: pytest plugin and testing helpers.
|
|
115
|
+
|
|
116
|
+
## Quick start
|
|
117
|
+
|
|
118
|
+
`setup()` is the recommended entrypoint when you want one line to configure sinks, database instrumentation, and framework integration:
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
from fastapi import FastAPI
|
|
122
|
+
from sqlalchemy import create_engine
|
|
123
|
+
import queryargus
|
|
124
|
+
|
|
125
|
+
app = FastAPI()
|
|
126
|
+
engine = create_engine("sqlite:///app.db")
|
|
127
|
+
|
|
128
|
+
queryargus.setup(
|
|
129
|
+
adapter=engine,
|
|
130
|
+
framework=app,
|
|
131
|
+
sink="jsonl",
|
|
132
|
+
)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
With that in place:
|
|
136
|
+
|
|
137
|
+
- the SQLAlchemy adapter captures queries automatically
|
|
138
|
+
- the FastAPI middleware opens one trace per request
|
|
139
|
+
- traces are appended to `traces/queryargus.jsonl`
|
|
140
|
+
|
|
141
|
+
## Core usage patterns
|
|
142
|
+
|
|
143
|
+
### FastAPI or Starlette request tracing
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
from fastapi import FastAPI
|
|
147
|
+
from sqlalchemy import create_engine
|
|
148
|
+
import queryargus
|
|
149
|
+
|
|
150
|
+
app = FastAPI()
|
|
151
|
+
engine = create_engine("sqlite:///inventory.db")
|
|
152
|
+
|
|
153
|
+
queryargus.setup(adapter=engine, framework=app, sink="jsonl")
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Each HTTP request becomes a root trace named like `GET /products`.
|
|
157
|
+
|
|
158
|
+
### Jobs, workers, and CLI functions
|
|
159
|
+
|
|
160
|
+
Use `@traced` when the function itself is the unit of work:
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
import queryargus
|
|
164
|
+
|
|
165
|
+
queryargus.setup(adapter=engine, sink="jsonl")
|
|
166
|
+
|
|
167
|
+
@queryargus.traced("billing.process_invoice")
|
|
168
|
+
def process_invoice(invoice_id: str) -> None:
|
|
169
|
+
repository.load_invoice(invoice_id)
|
|
170
|
+
service.apply_rules(invoice_id)
|
|
171
|
+
repository.mark_processed(invoice_id)
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
If a trace is already active, `@traced` does not open a nested root trace.
|
|
175
|
+
|
|
176
|
+
### Manual tracing
|
|
177
|
+
|
|
178
|
+
Use `start_trace()` when a context manager fits better than a decorator:
|
|
179
|
+
|
|
180
|
+
```python
|
|
181
|
+
from queryargus import start_trace, trace_scope
|
|
182
|
+
|
|
183
|
+
with start_trace("inventory.rebuild_projection"):
|
|
184
|
+
with trace_scope("inventory_repository.load_snapshot"):
|
|
185
|
+
repository.load_snapshot()
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Important behavior:
|
|
189
|
+
|
|
190
|
+
- `start_trace()` raises `NestedTraceError` if a root trace is already active.
|
|
191
|
+
- `trace_scope()` is a no-op when there is no active trace.
|
|
192
|
+
|
|
193
|
+
### Automatic business scopes on repositories and services
|
|
194
|
+
|
|
195
|
+
Instrument a class:
|
|
196
|
+
|
|
197
|
+
```python
|
|
198
|
+
from queryargus import trace_methods
|
|
199
|
+
|
|
200
|
+
@trace_methods()
|
|
201
|
+
class StockRepository:
|
|
202
|
+
def get_by_id(self, stock_id: str):
|
|
203
|
+
...
|
|
204
|
+
|
|
205
|
+
def list_all(self):
|
|
206
|
+
...
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Instrument an existing instance:
|
|
210
|
+
|
|
211
|
+
```python
|
|
212
|
+
from queryargus import instrument_object_methods
|
|
213
|
+
|
|
214
|
+
repository = instrument_object_methods(repository, namespace="products")
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
That gives you scope labels such as `StockRepository.get_by_id` or `products.list_all` in reports.
|
|
218
|
+
|
|
219
|
+
## Supported adapters and integrations
|
|
220
|
+
|
|
221
|
+
### SQLAlchemy
|
|
222
|
+
|
|
223
|
+
Auto-detected by `setup(adapter=engine)` or explicitly installed:
|
|
224
|
+
|
|
225
|
+
```python
|
|
226
|
+
from queryargus.adapters import instrument_sqlalchemy
|
|
227
|
+
|
|
228
|
+
instrument_sqlalchemy(engine)
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### psycopg 3
|
|
232
|
+
|
|
233
|
+
```python
|
|
234
|
+
from queryargus.adapters import instrument_psycopg
|
|
235
|
+
|
|
236
|
+
conn = instrument_psycopg(conn)
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### asyncpg
|
|
240
|
+
|
|
241
|
+
```python
|
|
242
|
+
from queryargus.adapters import instrument_asyncpg
|
|
243
|
+
|
|
244
|
+
conn = instrument_asyncpg(conn)
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### FastAPI and Starlette
|
|
248
|
+
|
|
249
|
+
Auto-detected by `setup(framework=app)` or explicitly installed:
|
|
250
|
+
|
|
251
|
+
```python
|
|
252
|
+
from queryargus.integrations.fastapi import instrument_fastapi
|
|
253
|
+
from queryargus.integrations.starlette import instrument_starlette
|
|
254
|
+
|
|
255
|
+
instrument_fastapi(app)
|
|
256
|
+
instrument_starlette(app)
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## Sinks
|
|
260
|
+
|
|
261
|
+
QueryArgus ships with two built-in sinks:
|
|
262
|
+
|
|
263
|
+
- `JsonlSink`: appends one serialized trace per line to a JSONL file
|
|
264
|
+
- `MemorySink`: keeps traces in memory for tests and local experiments
|
|
265
|
+
|
|
266
|
+
Examples:
|
|
267
|
+
|
|
268
|
+
```python
|
|
269
|
+
import queryargus
|
|
270
|
+
|
|
271
|
+
queryargus.setup(sink="jsonl")
|
|
272
|
+
queryargus.setup(sink="memory")
|
|
273
|
+
queryargus.setup(sink=[queryargus.MemorySink(), queryargus.JsonlSink("var/traces.jsonl")])
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
When you omit `sink`, QueryArgus defaults to `JsonlSink("traces/queryargus.jsonl")`.
|
|
277
|
+
|
|
278
|
+
## Testing support
|
|
279
|
+
|
|
280
|
+
Install the testing extra:
|
|
281
|
+
|
|
282
|
+
```bash
|
|
283
|
+
pip install "queryargus[testing]"
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
Pytest fixtures exposed through the `pytest11` entry point:
|
|
287
|
+
|
|
288
|
+
- `queryargus_memory_sink`
|
|
289
|
+
- `queryargus_trace`
|
|
290
|
+
- `queryargus_captured`
|
|
291
|
+
|
|
292
|
+
Useful helpers:
|
|
293
|
+
|
|
294
|
+
```python
|
|
295
|
+
from queryargus.testing import (
|
|
296
|
+
assert_all_queries_scoped,
|
|
297
|
+
assert_max_duration_ms,
|
|
298
|
+
assert_no_n_plus_one,
|
|
299
|
+
assert_no_repeated_queries,
|
|
300
|
+
assert_query_count_at_most,
|
|
301
|
+
capture_queries,
|
|
302
|
+
)
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
Example:
|
|
306
|
+
|
|
307
|
+
```python
|
|
308
|
+
def test_repository_is_efficient(queryargus_trace):
|
|
309
|
+
repository.list_products()
|
|
310
|
+
|
|
311
|
+
assert_query_count_at_most(queryargus_trace, 3)
|
|
312
|
+
assert_no_n_plus_one(queryargus_trace)
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
## Offline reports
|
|
316
|
+
|
|
317
|
+
Generate a report from a JSONL trace file:
|
|
318
|
+
|
|
319
|
+
```bash
|
|
320
|
+
queryargus-report traces/queryargus.jsonl
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
Supported formats:
|
|
324
|
+
|
|
325
|
+
- `text`
|
|
326
|
+
- `json`
|
|
327
|
+
- `html`
|
|
328
|
+
- `badge`
|
|
329
|
+
- `summary`
|
|
330
|
+
|
|
331
|
+
Examples:
|
|
332
|
+
|
|
333
|
+
```bash
|
|
334
|
+
queryargus-report traces/queryargus.jsonl --format html --output artifacts/queryargus-report
|
|
335
|
+
queryargus-report traces/queryargus.jsonl --format badge --output artifacts/queryargus-coverage.svg
|
|
336
|
+
queryargus-report traces/queryargus.jsonl --format summary --output artifacts/queryargus-summary.md
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
The HTML report includes coverage summaries, entrypoint breakdowns, hotspot analysis, and N+1 detection.
|
|
340
|
+
|
|
341
|
+
## Documentation
|
|
342
|
+
|
|
343
|
+
Detailed English documentation:
|
|
344
|
+
|
|
345
|
+
- [Documentation index](https://github.com/matheuss0xf/queryargus/blob/main/docs/en/index.md)
|
|
346
|
+
- [User guide](https://github.com/matheuss0xf/queryargus/blob/main/docs/en/user-guide.md)
|
|
347
|
+
- [API reference](https://github.com/matheuss0xf/queryargus/blob/main/docs/en/api-reference.md)
|
|
348
|
+
- [Publishing guide](https://github.com/matheuss0xf/queryargus/blob/main/docs/en/publishing.md)
|
|
349
|
+
- [Troubleshooting](https://github.com/matheuss0xf/queryargus/blob/main/docs/en/troubleshooting.md)
|
|
350
|
+
|
|
351
|
+
Detailed Brazilian Portuguese documentation:
|
|
352
|
+
|
|
353
|
+
- [Índice da documentação](https://github.com/matheuss0xf/queryargus/blob/main/docs/pt-BR/index.md)
|
|
354
|
+
- [Guia de uso](https://github.com/matheuss0xf/queryargus/blob/main/docs/pt-BR/guia-de-uso.md)
|
|
355
|
+
- [Referência da API](https://github.com/matheuss0xf/queryargus/blob/main/docs/pt-BR/referencia-da-api.md)
|
|
356
|
+
- [Guia de publicação](https://github.com/matheuss0xf/queryargus/blob/main/docs/pt-BR/publicacao.md)
|
|
357
|
+
- [Solução de problemas](https://github.com/matheuss0xf/queryargus/blob/main/docs/pt-BR/solucao-de-problemas.md)
|
|
358
|
+
|
|
359
|
+
## Compatibility
|
|
360
|
+
|
|
361
|
+
- Python `>=3.10,<3.15`
|
|
362
|
+
- Typed package (`py.typed` included)
|
|
363
|
+
- Offline-first JSONL workflow by default
|
|
364
|
+
- Designed for application code, tests, and CI pipelines
|
|
365
|
+
|
|
366
|
+
## Development status
|
|
367
|
+
|
|
368
|
+
The package is ready for early professional usage and packaging, with a deliberately small public API. The current release focuses on reliable trace capture, scope-aware analysis, and offline reporting.
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
# QueryArgus
|
|
2
|
+
|
|
3
|
+
See every query. Miss nothing.
|
|
4
|
+
|
|
5
|
+
QueryArgus is a Python library for tracing SQL queries across requests, jobs, tests, CLI commands, and regular functions. It captures query execution with business context, stores finished traces in simple offline-friendly formats, and helps you understand repeated queries, hotspots, and possible N+1 sequences without forcing an APM-shaped workflow on your application.
|
|
6
|
+
|
|
7
|
+
[Portuguese (Brazil) documentation](https://github.com/matheuss0xf/queryargus/blob/main/README.pt-BR.md)
|
|
8
|
+
|
|
9
|
+
## Why QueryArgus
|
|
10
|
+
|
|
11
|
+
Database behavior often becomes opaque as an application grows:
|
|
12
|
+
|
|
13
|
+
- N+1 issues slip into production unnoticed.
|
|
14
|
+
- Performance regressions show up late.
|
|
15
|
+
- Generic APMs reveal symptoms, but not the actual query flow inside the unit of work you care about.
|
|
16
|
+
- Plain logs rarely connect a query to the request, job, or repository method that caused it.
|
|
17
|
+
|
|
18
|
+
QueryArgus exists to make query behavior visible, understandable, and actionable.
|
|
19
|
+
|
|
20
|
+
## What it does
|
|
21
|
+
|
|
22
|
+
- Captures SQL queries through pluggable adapters.
|
|
23
|
+
- Associates queries with a root trace and nested business scopes.
|
|
24
|
+
- Infers a useful scope from caller code when you do not set one explicitly.
|
|
25
|
+
- Works in HTTP apps, background jobs, worker handlers, tests, and CLI code.
|
|
26
|
+
- Persists finished traces to JSONL or memory sinks.
|
|
27
|
+
- Generates offline reports in text, JSON, HTML, SVG badge, and Markdown summary formats.
|
|
28
|
+
- Provides pytest fixtures and assertions for query-aware tests.
|
|
29
|
+
|
|
30
|
+
## Installation
|
|
31
|
+
|
|
32
|
+
Install the core package:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install queryargus
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Install common integrations:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pip install "queryargus[sqlalchemy,fastapi,testing]"
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Available extras:
|
|
45
|
+
|
|
46
|
+
- `sqlalchemy`: SQLAlchemy `Engine` and `AsyncEngine` instrumentation.
|
|
47
|
+
- `psycopg`: psycopg 3 sync and async connection instrumentation.
|
|
48
|
+
- `asyncpg`: asyncpg connection instrumentation.
|
|
49
|
+
- `fastapi`: FastAPI and Starlette middleware integration.
|
|
50
|
+
- `testing`: pytest plugin and testing helpers.
|
|
51
|
+
|
|
52
|
+
## Quick start
|
|
53
|
+
|
|
54
|
+
`setup()` is the recommended entrypoint when you want one line to configure sinks, database instrumentation, and framework integration:
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
from fastapi import FastAPI
|
|
58
|
+
from sqlalchemy import create_engine
|
|
59
|
+
import queryargus
|
|
60
|
+
|
|
61
|
+
app = FastAPI()
|
|
62
|
+
engine = create_engine("sqlite:///app.db")
|
|
63
|
+
|
|
64
|
+
queryargus.setup(
|
|
65
|
+
adapter=engine,
|
|
66
|
+
framework=app,
|
|
67
|
+
sink="jsonl",
|
|
68
|
+
)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
With that in place:
|
|
72
|
+
|
|
73
|
+
- the SQLAlchemy adapter captures queries automatically
|
|
74
|
+
- the FastAPI middleware opens one trace per request
|
|
75
|
+
- traces are appended to `traces/queryargus.jsonl`
|
|
76
|
+
|
|
77
|
+
## Core usage patterns
|
|
78
|
+
|
|
79
|
+
### FastAPI or Starlette request tracing
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
from fastapi import FastAPI
|
|
83
|
+
from sqlalchemy import create_engine
|
|
84
|
+
import queryargus
|
|
85
|
+
|
|
86
|
+
app = FastAPI()
|
|
87
|
+
engine = create_engine("sqlite:///inventory.db")
|
|
88
|
+
|
|
89
|
+
queryargus.setup(adapter=engine, framework=app, sink="jsonl")
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Each HTTP request becomes a root trace named like `GET /products`.
|
|
93
|
+
|
|
94
|
+
### Jobs, workers, and CLI functions
|
|
95
|
+
|
|
96
|
+
Use `@traced` when the function itself is the unit of work:
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
import queryargus
|
|
100
|
+
|
|
101
|
+
queryargus.setup(adapter=engine, sink="jsonl")
|
|
102
|
+
|
|
103
|
+
@queryargus.traced("billing.process_invoice")
|
|
104
|
+
def process_invoice(invoice_id: str) -> None:
|
|
105
|
+
repository.load_invoice(invoice_id)
|
|
106
|
+
service.apply_rules(invoice_id)
|
|
107
|
+
repository.mark_processed(invoice_id)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
If a trace is already active, `@traced` does not open a nested root trace.
|
|
111
|
+
|
|
112
|
+
### Manual tracing
|
|
113
|
+
|
|
114
|
+
Use `start_trace()` when a context manager fits better than a decorator:
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
from queryargus import start_trace, trace_scope
|
|
118
|
+
|
|
119
|
+
with start_trace("inventory.rebuild_projection"):
|
|
120
|
+
with trace_scope("inventory_repository.load_snapshot"):
|
|
121
|
+
repository.load_snapshot()
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Important behavior:
|
|
125
|
+
|
|
126
|
+
- `start_trace()` raises `NestedTraceError` if a root trace is already active.
|
|
127
|
+
- `trace_scope()` is a no-op when there is no active trace.
|
|
128
|
+
|
|
129
|
+
### Automatic business scopes on repositories and services
|
|
130
|
+
|
|
131
|
+
Instrument a class:
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
from queryargus import trace_methods
|
|
135
|
+
|
|
136
|
+
@trace_methods()
|
|
137
|
+
class StockRepository:
|
|
138
|
+
def get_by_id(self, stock_id: str):
|
|
139
|
+
...
|
|
140
|
+
|
|
141
|
+
def list_all(self):
|
|
142
|
+
...
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Instrument an existing instance:
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
from queryargus import instrument_object_methods
|
|
149
|
+
|
|
150
|
+
repository = instrument_object_methods(repository, namespace="products")
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
That gives you scope labels such as `StockRepository.get_by_id` or `products.list_all` in reports.
|
|
154
|
+
|
|
155
|
+
## Supported adapters and integrations
|
|
156
|
+
|
|
157
|
+
### SQLAlchemy
|
|
158
|
+
|
|
159
|
+
Auto-detected by `setup(adapter=engine)` or explicitly installed:
|
|
160
|
+
|
|
161
|
+
```python
|
|
162
|
+
from queryargus.adapters import instrument_sqlalchemy
|
|
163
|
+
|
|
164
|
+
instrument_sqlalchemy(engine)
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### psycopg 3
|
|
168
|
+
|
|
169
|
+
```python
|
|
170
|
+
from queryargus.adapters import instrument_psycopg
|
|
171
|
+
|
|
172
|
+
conn = instrument_psycopg(conn)
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### asyncpg
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
from queryargus.adapters import instrument_asyncpg
|
|
179
|
+
|
|
180
|
+
conn = instrument_asyncpg(conn)
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### FastAPI and Starlette
|
|
184
|
+
|
|
185
|
+
Auto-detected by `setup(framework=app)` or explicitly installed:
|
|
186
|
+
|
|
187
|
+
```python
|
|
188
|
+
from queryargus.integrations.fastapi import instrument_fastapi
|
|
189
|
+
from queryargus.integrations.starlette import instrument_starlette
|
|
190
|
+
|
|
191
|
+
instrument_fastapi(app)
|
|
192
|
+
instrument_starlette(app)
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## Sinks
|
|
196
|
+
|
|
197
|
+
QueryArgus ships with two built-in sinks:
|
|
198
|
+
|
|
199
|
+
- `JsonlSink`: appends one serialized trace per line to a JSONL file
|
|
200
|
+
- `MemorySink`: keeps traces in memory for tests and local experiments
|
|
201
|
+
|
|
202
|
+
Examples:
|
|
203
|
+
|
|
204
|
+
```python
|
|
205
|
+
import queryargus
|
|
206
|
+
|
|
207
|
+
queryargus.setup(sink="jsonl")
|
|
208
|
+
queryargus.setup(sink="memory")
|
|
209
|
+
queryargus.setup(sink=[queryargus.MemorySink(), queryargus.JsonlSink("var/traces.jsonl")])
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
When you omit `sink`, QueryArgus defaults to `JsonlSink("traces/queryargus.jsonl")`.
|
|
213
|
+
|
|
214
|
+
## Testing support
|
|
215
|
+
|
|
216
|
+
Install the testing extra:
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
pip install "queryargus[testing]"
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
Pytest fixtures exposed through the `pytest11` entry point:
|
|
223
|
+
|
|
224
|
+
- `queryargus_memory_sink`
|
|
225
|
+
- `queryargus_trace`
|
|
226
|
+
- `queryargus_captured`
|
|
227
|
+
|
|
228
|
+
Useful helpers:
|
|
229
|
+
|
|
230
|
+
```python
|
|
231
|
+
from queryargus.testing import (
|
|
232
|
+
assert_all_queries_scoped,
|
|
233
|
+
assert_max_duration_ms,
|
|
234
|
+
assert_no_n_plus_one,
|
|
235
|
+
assert_no_repeated_queries,
|
|
236
|
+
assert_query_count_at_most,
|
|
237
|
+
capture_queries,
|
|
238
|
+
)
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Example:
|
|
242
|
+
|
|
243
|
+
```python
|
|
244
|
+
def test_repository_is_efficient(queryargus_trace):
|
|
245
|
+
repository.list_products()
|
|
246
|
+
|
|
247
|
+
assert_query_count_at_most(queryargus_trace, 3)
|
|
248
|
+
assert_no_n_plus_one(queryargus_trace)
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## Offline reports
|
|
252
|
+
|
|
253
|
+
Generate a report from a JSONL trace file:
|
|
254
|
+
|
|
255
|
+
```bash
|
|
256
|
+
queryargus-report traces/queryargus.jsonl
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Supported formats:
|
|
260
|
+
|
|
261
|
+
- `text`
|
|
262
|
+
- `json`
|
|
263
|
+
- `html`
|
|
264
|
+
- `badge`
|
|
265
|
+
- `summary`
|
|
266
|
+
|
|
267
|
+
Examples:
|
|
268
|
+
|
|
269
|
+
```bash
|
|
270
|
+
queryargus-report traces/queryargus.jsonl --format html --output artifacts/queryargus-report
|
|
271
|
+
queryargus-report traces/queryargus.jsonl --format badge --output artifacts/queryargus-coverage.svg
|
|
272
|
+
queryargus-report traces/queryargus.jsonl --format summary --output artifacts/queryargus-summary.md
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
The HTML report includes coverage summaries, entrypoint breakdowns, hotspot analysis, and N+1 detection.
|
|
276
|
+
|
|
277
|
+
## Documentation
|
|
278
|
+
|
|
279
|
+
Detailed English documentation:
|
|
280
|
+
|
|
281
|
+
- [Documentation index](https://github.com/matheuss0xf/queryargus/blob/main/docs/en/index.md)
|
|
282
|
+
- [User guide](https://github.com/matheuss0xf/queryargus/blob/main/docs/en/user-guide.md)
|
|
283
|
+
- [API reference](https://github.com/matheuss0xf/queryargus/blob/main/docs/en/api-reference.md)
|
|
284
|
+
- [Publishing guide](https://github.com/matheuss0xf/queryargus/blob/main/docs/en/publishing.md)
|
|
285
|
+
- [Troubleshooting](https://github.com/matheuss0xf/queryargus/blob/main/docs/en/troubleshooting.md)
|
|
286
|
+
|
|
287
|
+
Detailed Brazilian Portuguese documentation:
|
|
288
|
+
|
|
289
|
+
- [Índice da documentação](https://github.com/matheuss0xf/queryargus/blob/main/docs/pt-BR/index.md)
|
|
290
|
+
- [Guia de uso](https://github.com/matheuss0xf/queryargus/blob/main/docs/pt-BR/guia-de-uso.md)
|
|
291
|
+
- [Referência da API](https://github.com/matheuss0xf/queryargus/blob/main/docs/pt-BR/referencia-da-api.md)
|
|
292
|
+
- [Guia de publicação](https://github.com/matheuss0xf/queryargus/blob/main/docs/pt-BR/publicacao.md)
|
|
293
|
+
- [Solução de problemas](https://github.com/matheuss0xf/queryargus/blob/main/docs/pt-BR/solucao-de-problemas.md)
|
|
294
|
+
|
|
295
|
+
## Compatibility
|
|
296
|
+
|
|
297
|
+
- Python `>=3.10,<3.15`
|
|
298
|
+
- Typed package (`py.typed` included)
|
|
299
|
+
- Offline-first JSONL workflow by default
|
|
300
|
+
- Designed for application code, tests, and CI pipelines
|
|
301
|
+
|
|
302
|
+
## Development status
|
|
303
|
+
|
|
304
|
+
The package is ready for early professional usage and packaging, with a deliberately small public API. The current release focuses on reliable trace capture, scope-aware analysis, and offline reporting.
|