pyquerytracker 0.1.0__py3-none-any.whl
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.
- pyquerytracker/__init__.py +4 -0
- pyquerytracker/config.py +66 -0
- pyquerytracker/core.py +102 -0
- pyquerytracker-0.1.0.dist-info/METADATA +84 -0
- pyquerytracker-0.1.0.dist-info/RECORD +8 -0
- pyquerytracker-0.1.0.dist-info/WHEEL +5 -0
- pyquerytracker-0.1.0.dist-info/licenses/LICENSE +21 -0
- pyquerytracker-0.1.0.dist-info/top_level.txt +1 -0
pyquerytracker/config.py
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from typing import Optional
|
|
4
|
+
import logging
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ExportType(str, Enum):
|
|
8
|
+
"""
|
|
9
|
+
Enum representing supported export formats for query tracking logs.
|
|
10
|
+
|
|
11
|
+
Attributes:
|
|
12
|
+
JSON: Export logs in JSON format.
|
|
13
|
+
CSV: Export logs in CSV format.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
JSON = "json"
|
|
17
|
+
CSV = "csv"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class Config:
|
|
22
|
+
"""
|
|
23
|
+
Configuration settings for the query tracking system.
|
|
24
|
+
|
|
25
|
+
Attributes:
|
|
26
|
+
slow_log_threshold_ms (float): Threshold in milliseconds above which a query is considered slow.
|
|
27
|
+
Defaults to 100.0 ms.
|
|
28
|
+
slow_log_level (int): Logging level for slow query logs (e.g., logging.WARNING, logging.INFO).
|
|
29
|
+
Defaults to logging.WARNING.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
# TODO: Adding export functionality
|
|
33
|
+
slow_log_threshold_ms: float = 100.0
|
|
34
|
+
slow_log_level: int = logging.WARNING
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
_config: Config = Config()
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def configure(
|
|
41
|
+
slow_log_threshold_ms: Optional[float] = None,
|
|
42
|
+
slow_log_level: Optional[int] = None,
|
|
43
|
+
):
|
|
44
|
+
"""
|
|
45
|
+
Configure global settings for query tracking.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
slow_log_threshold_ms (Optional[float]): Threshold in milliseconds to log a query as "slow".
|
|
49
|
+
If not provided, defaults to 100.0 ms.
|
|
50
|
+
slow_log_level (Optional[int]): Logging level for slow queries (e.g., logging.INFO, logging.WARNING).
|
|
51
|
+
If not provided, defaults to logging.WARNING.
|
|
52
|
+
"""
|
|
53
|
+
if slow_log_threshold_ms is not None:
|
|
54
|
+
_config.slow_log_threshold_ms = slow_log_threshold_ms
|
|
55
|
+
if slow_log_level is not None:
|
|
56
|
+
_config.slow_log_level = slow_log_level
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def get_config() -> Config:
|
|
60
|
+
"""
|
|
61
|
+
Retrieve the current query tracking configuration.
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
TrackerConfig: The current configuration settings.
|
|
65
|
+
"""
|
|
66
|
+
return _config
|
pyquerytracker/core.py
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import time
|
|
2
|
+
import logging
|
|
3
|
+
from functools import update_wrapper
|
|
4
|
+
from typing import Any, Callable, TypeVar, Generic
|
|
5
|
+
from pyquerytracker.config import get_config
|
|
6
|
+
|
|
7
|
+
# Set up logger
|
|
8
|
+
logger = logging.getLogger("pyquerytracker")
|
|
9
|
+
if not logger.handlers:
|
|
10
|
+
handler = logging.StreamHandler()
|
|
11
|
+
formatter = logging.Formatter(
|
|
12
|
+
"%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
|
13
|
+
)
|
|
14
|
+
handler.setFormatter(formatter)
|
|
15
|
+
logger.addHandler(handler)
|
|
16
|
+
logger.setLevel(logging.INFO)
|
|
17
|
+
|
|
18
|
+
T = TypeVar("T")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class TrackQuery(Generic[T]):
|
|
22
|
+
"""
|
|
23
|
+
Class-based decorator to track and log the execution time of functions or methods.
|
|
24
|
+
|
|
25
|
+
Logs include:
|
|
26
|
+
- Function name
|
|
27
|
+
- Class name (if method)
|
|
28
|
+
- Execution time (ms)
|
|
29
|
+
- Arguments
|
|
30
|
+
- Errors (if any)
|
|
31
|
+
|
|
32
|
+
Usage:
|
|
33
|
+
@TrackQuery()
|
|
34
|
+
def my_function():
|
|
35
|
+
...
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
def __init__(self) -> None:
|
|
39
|
+
self.config = get_config()
|
|
40
|
+
|
|
41
|
+
def __call__(self, func: Callable[..., T]) -> Callable[..., T]:
|
|
42
|
+
def wrapped(*args: Any, **kwargs: Any) -> T:
|
|
43
|
+
start = time.perf_counter()
|
|
44
|
+
class_name = None
|
|
45
|
+
|
|
46
|
+
# Try to detect if this is an instance or class method
|
|
47
|
+
if args:
|
|
48
|
+
possible_self_or_cls = args[0]
|
|
49
|
+
if hasattr(possible_self_or_cls, "__class__"):
|
|
50
|
+
if isinstance(possible_self_or_cls, type):
|
|
51
|
+
# classmethod
|
|
52
|
+
class_name = possible_self_or_cls.__name__
|
|
53
|
+
else:
|
|
54
|
+
# instance method
|
|
55
|
+
class_name = possible_self_or_cls.__class__.__name__
|
|
56
|
+
|
|
57
|
+
try:
|
|
58
|
+
result = func(*args, **kwargs)
|
|
59
|
+
duration = (time.perf_counter() - start) * 1000
|
|
60
|
+
if duration > self.config.slow_log_threshold_ms:
|
|
61
|
+
logger.log(
|
|
62
|
+
self.config.slow_log_level,
|
|
63
|
+
f"{class_name}.{func.__name__} -> Slow execution: took %.2fms",
|
|
64
|
+
duration,
|
|
65
|
+
)
|
|
66
|
+
# logger.warning("Slow execution: %s took %.2fms", func.__name__, duration)
|
|
67
|
+
else:
|
|
68
|
+
logger.info(
|
|
69
|
+
"Function %s%s executed successfully in %.2fms",
|
|
70
|
+
f"{class_name}." if class_name else "",
|
|
71
|
+
func.__name__,
|
|
72
|
+
duration,
|
|
73
|
+
extra={
|
|
74
|
+
"function_name": func.__name__,
|
|
75
|
+
"class_name": class_name,
|
|
76
|
+
"duration_ms": duration,
|
|
77
|
+
"func_args": args,
|
|
78
|
+
"func_kwargs": kwargs,
|
|
79
|
+
},
|
|
80
|
+
)
|
|
81
|
+
return result
|
|
82
|
+
except Exception as e:
|
|
83
|
+
duration = (time.perf_counter() - start) * 1000
|
|
84
|
+
logger.error(
|
|
85
|
+
"Function %s%s failed after %.2fms: %s",
|
|
86
|
+
f"{class_name}." if class_name else "",
|
|
87
|
+
func.__name__,
|
|
88
|
+
duration,
|
|
89
|
+
str(e),
|
|
90
|
+
exc_info=True,
|
|
91
|
+
extra={
|
|
92
|
+
"function_name": func.__name__,
|
|
93
|
+
"class_name": class_name,
|
|
94
|
+
"duration_ms": duration,
|
|
95
|
+
"func_args": args,
|
|
96
|
+
"func_kwargs": kwargs,
|
|
97
|
+
"error": str(e),
|
|
98
|
+
},
|
|
99
|
+
)
|
|
100
|
+
raise
|
|
101
|
+
|
|
102
|
+
return update_wrapper(wrapped, func)
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pyquerytracker
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A lightweight, decorator-based query performance tracking library for Python applications. Monitor and analyze database query performance with ease.
|
|
5
|
+
Author-email: MuddyHope <daktarisun@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Repository, https://github.com/MuddyHope/pyquerytracker
|
|
8
|
+
Project-URL: Issues, https://github.com/MuddyHope/pyquerytracker/issues
|
|
9
|
+
Keywords: performance,database,query,decorator,tracking
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
18
|
+
Requires-Python: >=3.11
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Dynamic: license-file
|
|
22
|
+
|
|
23
|
+
# 🐍 pyquerytracker
|
|
24
|
+
|
|
25
|
+
**pyquerytracker** is a lightweight Python utility to **track and analyze database query performance** using simple decorators. It enables developers to gain visibility into SQL execution time, log metadata, and export insights in JSON format — with optional FastAPI integration and scheduled reporting.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## 🚀 Features
|
|
30
|
+
|
|
31
|
+
- ✅ Easy-to-use decorator to track function execution (e.g., SQL queries)
|
|
32
|
+
- ✅ Capture runtime, function name, args, return values, and more
|
|
33
|
+
|
|
34
|
+
## TODO Features
|
|
35
|
+
- ✅ Export logs to JSON or CSV
|
|
36
|
+
- ✅ FastAPI integration to expose tracked metrics via REST API
|
|
37
|
+
- ✅ Schedule periodic exports using `APScheduler`
|
|
38
|
+
- ✅ Plug-and-play with any Python database client (SQLAlchemy, psycopg2, etc.)
|
|
39
|
+
- ✅ Modular and extensible design
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## 📦 Installation
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
pip install pyquerytracker
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Usage
|
|
50
|
+
### Basic Usage
|
|
51
|
+
```python
|
|
52
|
+
import time
|
|
53
|
+
from pyquerytracker import TrackQuery
|
|
54
|
+
|
|
55
|
+
@TrackQuery()
|
|
56
|
+
def run_query():
|
|
57
|
+
time.sleep(0.3) # Simulate SQL execution
|
|
58
|
+
return "SELECT * FROM users;"
|
|
59
|
+
|
|
60
|
+
run_query()
|
|
61
|
+
```
|
|
62
|
+
### Output
|
|
63
|
+
```bash
|
|
64
|
+
2025-06-14 14:23:00,123 - pyquerytracker - INFO - Function run_query executed successfully in 305.12ms
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### With Configure
|
|
68
|
+
```
|
|
69
|
+
import logging
|
|
70
|
+
from pyquerytracker.config import configure
|
|
71
|
+
|
|
72
|
+
configure(
|
|
73
|
+
slow_log_threshold_ms=200, # Log queries slower than 200ms
|
|
74
|
+
slow_log_level=logging.DEBUG # Use DEBUG level for slow logs
|
|
75
|
+
)
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Output
|
|
79
|
+
```bash
|
|
80
|
+
2025-06-14 14:24:45,456 - pyquerytracker - WARNING - Slow execution: run_query took 501.87ms
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
pyquerytracker/__init__.py,sha256=pYyF2RPTTMhA0SakZTZ_dJ5dbPOIPYlacXJg7dSfsdc,98
|
|
2
|
+
pyquerytracker/config.py,sha256=GuMaHx4RB77FFad4bzZTQ-EyQYkDYHRrELHD_bTjAZo,1907
|
|
3
|
+
pyquerytracker/core.py,sha256=eLMfYs41ajLRjoxtxKjyi9G3NPN7tpwYkNerccL2oM4,3583
|
|
4
|
+
pyquerytracker-0.1.0.dist-info/licenses/LICENSE,sha256=lXcEFZRxovixBqp9SYJRLrN5OpP6AMggc_v7eaAMWn4,1065
|
|
5
|
+
pyquerytracker-0.1.0.dist-info/METADATA,sha256=RlPCS75bqssay-luqk6PuGlz_sb6NKAEKlcixWokI8E,2540
|
|
6
|
+
pyquerytracker-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
7
|
+
pyquerytracker-0.1.0.dist-info/top_level.txt,sha256=E5kxHgMifJIw-S2IjZqA1XuAU06Vbhd7geNfRZgjZuQ,15
|
|
8
|
+
pyquerytracker-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 MuddyHope
|
|
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 @@
|
|
|
1
|
+
pyquerytracker
|