sherlock-ai 1.0.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.
- sherlock_ai-1.0.0/LICENSE +21 -0
- sherlock_ai-1.0.0/PKG-INFO +239 -0
- sherlock_ai-1.0.0/README.md +214 -0
- sherlock_ai-1.0.0/pyproject.toml +34 -0
- sherlock_ai-1.0.0/setup.cfg +4 -0
- sherlock_ai-1.0.0/src/sherlock_ai/__init__.py +19 -0
- sherlock_ai-1.0.0/src/sherlock_ai/logging_config.py +142 -0
- sherlock_ai-1.0.0/src/sherlock_ai/performance.py +166 -0
- sherlock_ai-1.0.0/src/sherlock_ai/utils/__init__.py +0 -0
- sherlock_ai-1.0.0/src/sherlock_ai/utils/helper.py +27 -0
- sherlock_ai-1.0.0/src/sherlock_ai.egg-info/PKG-INFO +239 -0
- sherlock_ai-1.0.0/src/sherlock_ai.egg-info/SOURCES.txt +13 -0
- sherlock_ai-1.0.0/src/sherlock_ai.egg-info/dependency_links.txt +1 -0
- sherlock_ai-1.0.0/src/sherlock_ai.egg-info/top_level.txt +1 -0
- sherlock_ai-1.0.0/tests/test_local.py +89 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Pranaw Mishra
|
|
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,239 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sherlock-ai
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: A Python package for performance monitoring and logging utilities
|
|
5
|
+
Author-email: Pranaw Mishra <pranawmishra73@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/pranawmishra/sherlock-ai.git
|
|
8
|
+
Project-URL: Repository, https://github.com/pranawmishra/sherlock-ai.git
|
|
9
|
+
Keywords: performance,monitoring,logging,debugging,profiling
|
|
10
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
+
Classifier: Topic :: System :: Monitoring
|
|
21
|
+
Requires-Python: >=3.8
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
|
|
26
|
+
# Sherlock AI
|
|
27
|
+
|
|
28
|
+
A Python package for performance monitoring and logging utilities that helps you track execution times and debug your applications with ease.
|
|
29
|
+
|
|
30
|
+
## Features
|
|
31
|
+
|
|
32
|
+
- ๐ฏ **Performance Decorators**: Easy-to-use decorators for tracking function execution times
|
|
33
|
+
- โฑ๏ธ **Context Managers**: Monitor code block execution with simple context managers
|
|
34
|
+
- ๐ง **Flexible Configuration**: Customizable logging levels, minimum duration thresholds, and argument logging
|
|
35
|
+
- ๐ **Async/Sync Support**: Works seamlessly with both synchronous and asynchronous functions
|
|
36
|
+
- ๐ **Request Tracking**: Built-in request ID tracking for distributed systems
|
|
37
|
+
- ๐ **Zero Dependencies**: Lightweight with minimal external dependencies
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pip install sherlock-ai
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Quick Start
|
|
46
|
+
|
|
47
|
+
### Basic Setup
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
from sherlock_ai.logging_config import setup_logging
|
|
51
|
+
from sherlock_ai.performance import log_performance
|
|
52
|
+
|
|
53
|
+
# Initialize logging (call once at application startup)
|
|
54
|
+
setup_logging()
|
|
55
|
+
|
|
56
|
+
@log_performance
|
|
57
|
+
def my_function():
|
|
58
|
+
# Your code here
|
|
59
|
+
time.sleep(1)
|
|
60
|
+
return "result"
|
|
61
|
+
|
|
62
|
+
# This will log: PERFORMANCE | my_module.my_function | SUCCESS | 1.003s
|
|
63
|
+
result = my_function()
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Advanced Configuration
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
@log_performance(min_duration=0.1, include_args=True, log_level="DEBUG")
|
|
70
|
+
def slow_database_query(user_id, limit=10):
|
|
71
|
+
# Only logs if execution time >= 0.1 seconds
|
|
72
|
+
# Includes function arguments in the log
|
|
73
|
+
pass
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Context Manager for Code Blocks
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
from sherlock_ai.performance import PerformanceTimer
|
|
80
|
+
|
|
81
|
+
with PerformanceTimer("database_operation"):
|
|
82
|
+
# Your code block here
|
|
83
|
+
result = database.query("SELECT * FROM users")
|
|
84
|
+
|
|
85
|
+
# Logs: PERFORMANCE | database_operation | SUCCESS | 0.234s
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Async Function Support
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
@log_performance
|
|
92
|
+
async def async_api_call():
|
|
93
|
+
async with httpx.AsyncClient() as client:
|
|
94
|
+
response = await client.get("https://api.example.com")
|
|
95
|
+
return response.json()
|
|
96
|
+
|
|
97
|
+
# Works automatically with async functions
|
|
98
|
+
result = await async_api_call()
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Manual Time Logging
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
from sherlock_ai.performance import log_execution_time
|
|
105
|
+
import time
|
|
106
|
+
|
|
107
|
+
start_time = time.time()
|
|
108
|
+
try:
|
|
109
|
+
# Your code here
|
|
110
|
+
result = complex_operation()
|
|
111
|
+
log_execution_time("complex_operation", start_time, success=True)
|
|
112
|
+
except Exception as e:
|
|
113
|
+
log_execution_time("complex_operation", start_time, success=False, error=str(e))
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## API Reference
|
|
117
|
+
|
|
118
|
+
### `@log_performance` Decorator
|
|
119
|
+
|
|
120
|
+
Parameters:
|
|
121
|
+
- `min_duration` (float): Only log if execution time >= this value in seconds (default: 0.0)
|
|
122
|
+
- `include_args` (bool): Whether to include function arguments in the log (default: False)
|
|
123
|
+
- `log_level` (str): Log level to use - INFO, DEBUG, WARNING, etc. (default: "INFO")
|
|
124
|
+
|
|
125
|
+
### `PerformanceTimer` Context Manager
|
|
126
|
+
|
|
127
|
+
Parameters:
|
|
128
|
+
- `name` (str): Name identifier for the operation
|
|
129
|
+
- `min_duration` (float): Only log if execution time >= this value in seconds (default: 0.0)
|
|
130
|
+
|
|
131
|
+
### `log_execution_time` Function
|
|
132
|
+
|
|
133
|
+
Parameters:
|
|
134
|
+
- `name` (str): Name identifier for the operation
|
|
135
|
+
- `start_time` (float): Start time from `time.time()`
|
|
136
|
+
- `success` (bool): Whether the operation succeeded (default: True)
|
|
137
|
+
- `error` (str): Error message if operation failed (default: None)
|
|
138
|
+
|
|
139
|
+
## Configuration
|
|
140
|
+
|
|
141
|
+
### Logging Setup
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
from sherlock_ai.logging_config import setup_logging, get_logger
|
|
145
|
+
|
|
146
|
+
# Initialize logging (call once at application startup)
|
|
147
|
+
setup_logging()
|
|
148
|
+
|
|
149
|
+
# Get a logger for your module
|
|
150
|
+
logger = get_logger(__name__)
|
|
151
|
+
|
|
152
|
+
# Use the logger
|
|
153
|
+
logger.info("Application started")
|
|
154
|
+
logger.error("Something went wrong")
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**Log Files Created:**
|
|
158
|
+
When you call `setup_logging()`, it automatically creates a `logs/` directory with these files:
|
|
159
|
+
- `app.log` - All INFO+ level logs
|
|
160
|
+
- `errors.log` - Only ERROR+ level logs
|
|
161
|
+
- `api.log` - API-related logs
|
|
162
|
+
- `database.log` - Database operation logs
|
|
163
|
+
- `services.log` - Service operation logs
|
|
164
|
+
- `performance.log` - Performance monitoring logs
|
|
165
|
+
|
|
166
|
+
### Request ID Tracking
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
from sherlock_ai.utils.helper import get_request_id
|
|
170
|
+
|
|
171
|
+
# Get current request ID for distributed tracing
|
|
172
|
+
request_id = get_request_id()
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Complete Application Example
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
from sherlock_ai.logging_config import setup_logging, get_logger
|
|
179
|
+
from sherlock_ai.performance import log_performance, PerformanceTimer
|
|
180
|
+
|
|
181
|
+
# Initialize logging first
|
|
182
|
+
setup_logging()
|
|
183
|
+
logger = get_logger(__name__)
|
|
184
|
+
|
|
185
|
+
@log_performance
|
|
186
|
+
def main():
|
|
187
|
+
logger.info("Application starting")
|
|
188
|
+
|
|
189
|
+
with PerformanceTimer("initialization"):
|
|
190
|
+
# Your initialization code
|
|
191
|
+
pass
|
|
192
|
+
|
|
193
|
+
logger.info("Application ready")
|
|
194
|
+
|
|
195
|
+
if __name__ == "__main__":
|
|
196
|
+
main()
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Log Output Format
|
|
200
|
+
|
|
201
|
+
The package produces structured log messages in the following format:
|
|
202
|
+
|
|
203
|
+
```
|
|
204
|
+
PERFORMANCE | {function_name} | {STATUS} | {execution_time}s | {additional_info}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Examples:
|
|
208
|
+
```
|
|
209
|
+
PERFORMANCE | my_module.my_function | SUCCESS | 0.123s
|
|
210
|
+
PERFORMANCE | api_call | ERROR | 2.456s | Connection timeout
|
|
211
|
+
PERFORMANCE | database_query | SUCCESS | 0.089s | Args: ('user123',) | Kwargs: {'limit': 10}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## Use Cases
|
|
215
|
+
|
|
216
|
+
- **API Performance Monitoring**: Track response times for your web APIs
|
|
217
|
+
- **Database Query Optimization**: Monitor slow database operations
|
|
218
|
+
- **Microservices Debugging**: Trace execution times across service boundaries
|
|
219
|
+
- **Algorithm Benchmarking**: Compare performance of different implementations
|
|
220
|
+
- **Production Monitoring**: Get insights into your application's performance characteristics
|
|
221
|
+
|
|
222
|
+
## Requirements
|
|
223
|
+
|
|
224
|
+
- Python >= 3.13
|
|
225
|
+
- Standard library only (no external dependencies)
|
|
226
|
+
|
|
227
|
+
## License
|
|
228
|
+
|
|
229
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
230
|
+
|
|
231
|
+
## Contributing
|
|
232
|
+
|
|
233
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
234
|
+
|
|
235
|
+
## Links
|
|
236
|
+
|
|
237
|
+
- **Homepage**: [https://github.com/pranawmishra/sherlock-ai](https://github.com/pranawmishra/sherlock-ai)
|
|
238
|
+
- **Repository**: [https://github.com/pranawmishra/sherlock-ai](https://github.com/pranawmishra/sherlock-ai)
|
|
239
|
+
- **Issues**: [https://github.com/pranawmishra/sherlock-ai/issues](https://github.com/pranawmishra/sherlock-ai/issues)
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# Sherlock AI
|
|
2
|
+
|
|
3
|
+
A Python package for performance monitoring and logging utilities that helps you track execution times and debug your applications with ease.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- ๐ฏ **Performance Decorators**: Easy-to-use decorators for tracking function execution times
|
|
8
|
+
- โฑ๏ธ **Context Managers**: Monitor code block execution with simple context managers
|
|
9
|
+
- ๐ง **Flexible Configuration**: Customizable logging levels, minimum duration thresholds, and argument logging
|
|
10
|
+
- ๐ **Async/Sync Support**: Works seamlessly with both synchronous and asynchronous functions
|
|
11
|
+
- ๐ **Request Tracking**: Built-in request ID tracking for distributed systems
|
|
12
|
+
- ๐ **Zero Dependencies**: Lightweight with minimal external dependencies
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pip install sherlock-ai
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Quick Start
|
|
21
|
+
|
|
22
|
+
### Basic Setup
|
|
23
|
+
|
|
24
|
+
```python
|
|
25
|
+
from sherlock_ai.logging_config import setup_logging
|
|
26
|
+
from sherlock_ai.performance import log_performance
|
|
27
|
+
|
|
28
|
+
# Initialize logging (call once at application startup)
|
|
29
|
+
setup_logging()
|
|
30
|
+
|
|
31
|
+
@log_performance
|
|
32
|
+
def my_function():
|
|
33
|
+
# Your code here
|
|
34
|
+
time.sleep(1)
|
|
35
|
+
return "result"
|
|
36
|
+
|
|
37
|
+
# This will log: PERFORMANCE | my_module.my_function | SUCCESS | 1.003s
|
|
38
|
+
result = my_function()
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Advanced Configuration
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
@log_performance(min_duration=0.1, include_args=True, log_level="DEBUG")
|
|
45
|
+
def slow_database_query(user_id, limit=10):
|
|
46
|
+
# Only logs if execution time >= 0.1 seconds
|
|
47
|
+
# Includes function arguments in the log
|
|
48
|
+
pass
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Context Manager for Code Blocks
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
from sherlock_ai.performance import PerformanceTimer
|
|
55
|
+
|
|
56
|
+
with PerformanceTimer("database_operation"):
|
|
57
|
+
# Your code block here
|
|
58
|
+
result = database.query("SELECT * FROM users")
|
|
59
|
+
|
|
60
|
+
# Logs: PERFORMANCE | database_operation | SUCCESS | 0.234s
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Async Function Support
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
@log_performance
|
|
67
|
+
async def async_api_call():
|
|
68
|
+
async with httpx.AsyncClient() as client:
|
|
69
|
+
response = await client.get("https://api.example.com")
|
|
70
|
+
return response.json()
|
|
71
|
+
|
|
72
|
+
# Works automatically with async functions
|
|
73
|
+
result = await async_api_call()
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Manual Time Logging
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
from sherlock_ai.performance import log_execution_time
|
|
80
|
+
import time
|
|
81
|
+
|
|
82
|
+
start_time = time.time()
|
|
83
|
+
try:
|
|
84
|
+
# Your code here
|
|
85
|
+
result = complex_operation()
|
|
86
|
+
log_execution_time("complex_operation", start_time, success=True)
|
|
87
|
+
except Exception as e:
|
|
88
|
+
log_execution_time("complex_operation", start_time, success=False, error=str(e))
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## API Reference
|
|
92
|
+
|
|
93
|
+
### `@log_performance` Decorator
|
|
94
|
+
|
|
95
|
+
Parameters:
|
|
96
|
+
- `min_duration` (float): Only log if execution time >= this value in seconds (default: 0.0)
|
|
97
|
+
- `include_args` (bool): Whether to include function arguments in the log (default: False)
|
|
98
|
+
- `log_level` (str): Log level to use - INFO, DEBUG, WARNING, etc. (default: "INFO")
|
|
99
|
+
|
|
100
|
+
### `PerformanceTimer` Context Manager
|
|
101
|
+
|
|
102
|
+
Parameters:
|
|
103
|
+
- `name` (str): Name identifier for the operation
|
|
104
|
+
- `min_duration` (float): Only log if execution time >= this value in seconds (default: 0.0)
|
|
105
|
+
|
|
106
|
+
### `log_execution_time` Function
|
|
107
|
+
|
|
108
|
+
Parameters:
|
|
109
|
+
- `name` (str): Name identifier for the operation
|
|
110
|
+
- `start_time` (float): Start time from `time.time()`
|
|
111
|
+
- `success` (bool): Whether the operation succeeded (default: True)
|
|
112
|
+
- `error` (str): Error message if operation failed (default: None)
|
|
113
|
+
|
|
114
|
+
## Configuration
|
|
115
|
+
|
|
116
|
+
### Logging Setup
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
from sherlock_ai.logging_config import setup_logging, get_logger
|
|
120
|
+
|
|
121
|
+
# Initialize logging (call once at application startup)
|
|
122
|
+
setup_logging()
|
|
123
|
+
|
|
124
|
+
# Get a logger for your module
|
|
125
|
+
logger = get_logger(__name__)
|
|
126
|
+
|
|
127
|
+
# Use the logger
|
|
128
|
+
logger.info("Application started")
|
|
129
|
+
logger.error("Something went wrong")
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
**Log Files Created:**
|
|
133
|
+
When you call `setup_logging()`, it automatically creates a `logs/` directory with these files:
|
|
134
|
+
- `app.log` - All INFO+ level logs
|
|
135
|
+
- `errors.log` - Only ERROR+ level logs
|
|
136
|
+
- `api.log` - API-related logs
|
|
137
|
+
- `database.log` - Database operation logs
|
|
138
|
+
- `services.log` - Service operation logs
|
|
139
|
+
- `performance.log` - Performance monitoring logs
|
|
140
|
+
|
|
141
|
+
### Request ID Tracking
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
from sherlock_ai.utils.helper import get_request_id
|
|
145
|
+
|
|
146
|
+
# Get current request ID for distributed tracing
|
|
147
|
+
request_id = get_request_id()
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Complete Application Example
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
from sherlock_ai.logging_config import setup_logging, get_logger
|
|
154
|
+
from sherlock_ai.performance import log_performance, PerformanceTimer
|
|
155
|
+
|
|
156
|
+
# Initialize logging first
|
|
157
|
+
setup_logging()
|
|
158
|
+
logger = get_logger(__name__)
|
|
159
|
+
|
|
160
|
+
@log_performance
|
|
161
|
+
def main():
|
|
162
|
+
logger.info("Application starting")
|
|
163
|
+
|
|
164
|
+
with PerformanceTimer("initialization"):
|
|
165
|
+
# Your initialization code
|
|
166
|
+
pass
|
|
167
|
+
|
|
168
|
+
logger.info("Application ready")
|
|
169
|
+
|
|
170
|
+
if __name__ == "__main__":
|
|
171
|
+
main()
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Log Output Format
|
|
175
|
+
|
|
176
|
+
The package produces structured log messages in the following format:
|
|
177
|
+
|
|
178
|
+
```
|
|
179
|
+
PERFORMANCE | {function_name} | {STATUS} | {execution_time}s | {additional_info}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Examples:
|
|
183
|
+
```
|
|
184
|
+
PERFORMANCE | my_module.my_function | SUCCESS | 0.123s
|
|
185
|
+
PERFORMANCE | api_call | ERROR | 2.456s | Connection timeout
|
|
186
|
+
PERFORMANCE | database_query | SUCCESS | 0.089s | Args: ('user123',) | Kwargs: {'limit': 10}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## Use Cases
|
|
190
|
+
|
|
191
|
+
- **API Performance Monitoring**: Track response times for your web APIs
|
|
192
|
+
- **Database Query Optimization**: Monitor slow database operations
|
|
193
|
+
- **Microservices Debugging**: Trace execution times across service boundaries
|
|
194
|
+
- **Algorithm Benchmarking**: Compare performance of different implementations
|
|
195
|
+
- **Production Monitoring**: Get insights into your application's performance characteristics
|
|
196
|
+
|
|
197
|
+
## Requirements
|
|
198
|
+
|
|
199
|
+
- Python >= 3.13
|
|
200
|
+
- Standard library only (no external dependencies)
|
|
201
|
+
|
|
202
|
+
## License
|
|
203
|
+
|
|
204
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
205
|
+
|
|
206
|
+
## Contributing
|
|
207
|
+
|
|
208
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
209
|
+
|
|
210
|
+
## Links
|
|
211
|
+
|
|
212
|
+
- **Homepage**: [https://github.com/pranawmishra/sherlock-ai](https://github.com/pranawmishra/sherlock-ai)
|
|
213
|
+
- **Repository**: [https://github.com/pranawmishra/sherlock-ai](https://github.com/pranawmishra/sherlock-ai)
|
|
214
|
+
- **Issues**: [https://github.com/pranawmishra/sherlock-ai/issues](https://github.com/pranawmishra/sherlock-ai/issues)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=45", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "sherlock-ai"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "A Python package for performance monitoring and logging utilities"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
license = {text = "MIT"}
|
|
12
|
+
authors = [{name = "Pranaw Mishra", email = "pranawmishra73@gmail.com"}]
|
|
13
|
+
keywords = ["performance", "monitoring", "logging", "debugging", "profiling"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 5 - Production/Stable",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Programming Language :: Python :: 3.8",
|
|
20
|
+
"Programming Language :: Python :: 3.9",
|
|
21
|
+
"Programming Language :: Python :: 3.10",
|
|
22
|
+
"Programming Language :: Python :: 3.11",
|
|
23
|
+
"Programming Language :: Python :: 3.12",
|
|
24
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
25
|
+
"Topic :: System :: Monitoring",
|
|
26
|
+
]
|
|
27
|
+
dependencies = []
|
|
28
|
+
|
|
29
|
+
[project.urls]
|
|
30
|
+
Homepage = "https://github.com/pranawmishra/sherlock-ai.git"
|
|
31
|
+
Repository = "https://github.com/pranawmishra/sherlock-ai.git"
|
|
32
|
+
|
|
33
|
+
[tool.setuptools.packages.find]
|
|
34
|
+
where = ["src"]
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Sherlock AI - Your AI assistant package
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
__version__ = "1.0.0"
|
|
6
|
+
__author__ = "Pranaw Mishra"
|
|
7
|
+
__email__ = "pranawmishra73@gmail.com"
|
|
8
|
+
|
|
9
|
+
# Import main components for easy access
|
|
10
|
+
from .performance import log_performance, PerformanceTimer
|
|
11
|
+
from .logging_config import setup_logging, get_logger
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"log_performance",
|
|
15
|
+
"PerformanceTimer",
|
|
16
|
+
"setup_logging",
|
|
17
|
+
"get_logger",
|
|
18
|
+
"__version__"
|
|
19
|
+
]
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# app > core > logging_config.py
|
|
2
|
+
import logging
|
|
3
|
+
import logging.handlers
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from sherlock_ai.utils.helper import request_id_var
|
|
6
|
+
|
|
7
|
+
class RequestIdFormatter(logging.Formatter):
|
|
8
|
+
"""Custom formatter that includes request ID in log messages"""
|
|
9
|
+
|
|
10
|
+
def format(self, record):
|
|
11
|
+
"""Add request ID to log message"""
|
|
12
|
+
# get current request ID from context
|
|
13
|
+
record.request_id = request_id_var.get("") or "-"
|
|
14
|
+
return super().format(record)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def setup_logging():
|
|
18
|
+
"""Set up logging configuration for the application with request ID support"""
|
|
19
|
+
|
|
20
|
+
# Create logs directory if it doesn't exist
|
|
21
|
+
logs_dir = Path("logs")
|
|
22
|
+
logs_dir.mkdir(exist_ok=True)
|
|
23
|
+
|
|
24
|
+
# Configure logging format
|
|
25
|
+
log_format = "%(asctime)s - %(request_id)s - %(name)s - %(levelname)s - %(message)s"
|
|
26
|
+
date_format = "%Y-%m-%d %H:%M:%S"
|
|
27
|
+
|
|
28
|
+
# Create custom formatter with request ID support
|
|
29
|
+
formatter = RequestIdFormatter(log_format, datefmt=date_format)
|
|
30
|
+
|
|
31
|
+
# Clear existing handlers to avoid duplicates
|
|
32
|
+
logging.root.handlers.clear()
|
|
33
|
+
|
|
34
|
+
# 1. Console Handler - prints to terminal
|
|
35
|
+
console_handler = logging.StreamHandler()
|
|
36
|
+
console_handler.setLevel(logging.INFO)
|
|
37
|
+
console_handler.setFormatter(formatter)
|
|
38
|
+
|
|
39
|
+
# 2. Main App Log - all logs INFO and above
|
|
40
|
+
app_handler = logging.handlers.RotatingFileHandler(
|
|
41
|
+
"logs/app.log",
|
|
42
|
+
maxBytes=10*1024*1024, # 10MB
|
|
43
|
+
backupCount=5,
|
|
44
|
+
encoding="utf-8"
|
|
45
|
+
)
|
|
46
|
+
app_handler.setLevel(logging.INFO)
|
|
47
|
+
app_handler.setFormatter(formatter)
|
|
48
|
+
|
|
49
|
+
# 3. Error Log - only ERROR and CRITICAL logs
|
|
50
|
+
error_handler = logging.handlers.RotatingFileHandler(
|
|
51
|
+
"logs/errors.log",
|
|
52
|
+
maxBytes=10*1024*1024, # 10MB
|
|
53
|
+
backupCount=5,
|
|
54
|
+
encoding="utf-8"
|
|
55
|
+
)
|
|
56
|
+
error_handler.setLevel(logging.ERROR)
|
|
57
|
+
error_handler.setFormatter(formatter)
|
|
58
|
+
|
|
59
|
+
# 4. API Log - specifically for API-related logs
|
|
60
|
+
api_handler = logging.handlers.RotatingFileHandler(
|
|
61
|
+
"logs/api.log",
|
|
62
|
+
maxBytes=10*1024*1024, # 10MB
|
|
63
|
+
backupCount=5,
|
|
64
|
+
encoding="utf-8"
|
|
65
|
+
)
|
|
66
|
+
api_handler.setLevel(logging.INFO)
|
|
67
|
+
api_handler.setFormatter(formatter)
|
|
68
|
+
|
|
69
|
+
# 5. Database Log - specifically for database operations
|
|
70
|
+
db_handler = logging.handlers.RotatingFileHandler(
|
|
71
|
+
"logs/database.log",
|
|
72
|
+
maxBytes=10*1024*1024, # 10MB
|
|
73
|
+
backupCount=5,
|
|
74
|
+
encoding="utf-8"
|
|
75
|
+
)
|
|
76
|
+
db_handler.setLevel(logging.INFO)
|
|
77
|
+
db_handler.setFormatter(formatter)
|
|
78
|
+
|
|
79
|
+
# 6. Services Log - for all service-related operations
|
|
80
|
+
services_handler = logging.handlers.RotatingFileHandler(
|
|
81
|
+
"logs/services.log",
|
|
82
|
+
maxBytes=10*1024*1024, # 10MB
|
|
83
|
+
backupCount=5,
|
|
84
|
+
encoding="utf-8"
|
|
85
|
+
)
|
|
86
|
+
services_handler.setLevel(logging.INFO)
|
|
87
|
+
services_handler.setFormatter(formatter)
|
|
88
|
+
|
|
89
|
+
# 7. Performance Log - specifically for performance metrics
|
|
90
|
+
performance_handler = logging.handlers.RotatingFileHandler(
|
|
91
|
+
"logs/performance.log",
|
|
92
|
+
maxBytes=10*1024*1024, # 10MB
|
|
93
|
+
backupCount=5,
|
|
94
|
+
encoding="utf-8"
|
|
95
|
+
)
|
|
96
|
+
performance_handler.setLevel(logging.INFO)
|
|
97
|
+
performance_handler.setFormatter(formatter)
|
|
98
|
+
|
|
99
|
+
# Configure root logger with console, main app, and error handlers
|
|
100
|
+
logging.root.setLevel(logging.INFO)
|
|
101
|
+
logging.root.addHandler(console_handler)
|
|
102
|
+
logging.root.addHandler(app_handler)
|
|
103
|
+
logging.root.addHandler(error_handler)
|
|
104
|
+
|
|
105
|
+
# Configure specific loggers for different components
|
|
106
|
+
# API loggers - all API modules will log to api.log
|
|
107
|
+
api_logger = logging.getLogger("app.api")
|
|
108
|
+
api_logger.setLevel(logging.INFO)
|
|
109
|
+
api_logger.addHandler(api_handler)
|
|
110
|
+
|
|
111
|
+
# Database loggers - all database operations will log to database.log
|
|
112
|
+
db_logger = logging.getLogger("app.core.dbConnection")
|
|
113
|
+
db_logger.setLevel(logging.INFO)
|
|
114
|
+
db_logger.addHandler(db_handler)
|
|
115
|
+
|
|
116
|
+
# Services loggers - all services will log to services.log
|
|
117
|
+
services_logger = logging.getLogger("app.services")
|
|
118
|
+
services_logger.setLevel(logging.INFO)
|
|
119
|
+
services_logger.addHandler(services_handler)
|
|
120
|
+
|
|
121
|
+
# Performance loggers - all performance metrics will log to performance.log
|
|
122
|
+
performance_logger = logging.getLogger("PerformanceLogger")
|
|
123
|
+
performance_logger.setLevel(logging.INFO)
|
|
124
|
+
performance_logger.addHandler(performance_handler)
|
|
125
|
+
|
|
126
|
+
# Set specific log levels for external libraries
|
|
127
|
+
logging.getLogger("uvicorn").setLevel(logging.INFO)
|
|
128
|
+
logging.getLogger("fastapi").setLevel(logging.INFO)
|
|
129
|
+
|
|
130
|
+
# Prevent duplicate logs (don't propagate to parent if already handled)
|
|
131
|
+
api_logger.propagate = True # Still propagate to get in main app.log
|
|
132
|
+
db_logger.propagate = True
|
|
133
|
+
services_logger.propagate = True
|
|
134
|
+
performance_logger.propagate = False
|
|
135
|
+
|
|
136
|
+
# Helper function to get logger (optional, but clean)
|
|
137
|
+
def get_logger(name: str = None):
|
|
138
|
+
"""Get a logger. If no name provided, uses the caller's __name__."""
|
|
139
|
+
return logging.getLogger(name) if name else logging.getLogger(__name__)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
# setup_logging()
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# app > utils > performance.py
|
|
2
|
+
import time
|
|
3
|
+
import functools
|
|
4
|
+
import asyncio
|
|
5
|
+
import logging
|
|
6
|
+
from typing import Any, Callable, TypeVar, Union
|
|
7
|
+
from sherlock_ai.utils.helper import get_request_id
|
|
8
|
+
|
|
9
|
+
# Create a logger specifically for performance metrics
|
|
10
|
+
logger = logging.getLogger("PerformanceLogger")
|
|
11
|
+
|
|
12
|
+
# Type variable for better type hints
|
|
13
|
+
F = TypeVar("F", bound=Callable[..., Any])
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def log_performance(
|
|
17
|
+
func: F = None,
|
|
18
|
+
*,
|
|
19
|
+
min_duration: float = 0.0,
|
|
20
|
+
include_args: bool = False,
|
|
21
|
+
log_level: str = "INFO"
|
|
22
|
+
) -> Union[F, Callable[[F], F]]:
|
|
23
|
+
"""
|
|
24
|
+
Decorator to log function execution time
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
func: The function to decorate (when used without parentheses)
|
|
28
|
+
min_duration: Only log if execution time >= this value (in seconds)
|
|
29
|
+
include_args: Whether to include function arguments in the log
|
|
30
|
+
log_level: Log level to use (INFO, DEBUG, WARNING, etc.)
|
|
31
|
+
|
|
32
|
+
Usage:
|
|
33
|
+
@log_performance
|
|
34
|
+
def my_function():
|
|
35
|
+
pass
|
|
36
|
+
|
|
37
|
+
@log_performance(min_duration=0.1, include_args=True)
|
|
38
|
+
def slow_function(param1, param2):
|
|
39
|
+
pass
|
|
40
|
+
"""
|
|
41
|
+
def decorator(f: F) -> F:
|
|
42
|
+
@functools.wraps(f)
|
|
43
|
+
async def async_wrapper(*args, **kwargs):
|
|
44
|
+
start_time = time.time()
|
|
45
|
+
function_name = f"{f.__module__}.{f.__name__}"
|
|
46
|
+
request_id = get_request_id()
|
|
47
|
+
|
|
48
|
+
# Log function arguments if requested
|
|
49
|
+
args_info = ""
|
|
50
|
+
if include_args:
|
|
51
|
+
args_str = str(args)[:100] if args else ""
|
|
52
|
+
kwargs_str = str(kwargs)[:100] if kwargs else ""
|
|
53
|
+
args_info = f" | Args: {args_str} | Kwargs: {kwargs_str}"
|
|
54
|
+
|
|
55
|
+
try:
|
|
56
|
+
# Execute the async function
|
|
57
|
+
result = await f(*args, **kwargs)
|
|
58
|
+
execution_time = time.time() - start_time
|
|
59
|
+
|
|
60
|
+
# Only log if execution time meets minimum threshold
|
|
61
|
+
if execution_time >= min_duration:
|
|
62
|
+
log_method = getattr(logger, log_level.lower())
|
|
63
|
+
log_method(
|
|
64
|
+
f"PERFORMANCE | {function_name} | SUCCESS | {execution_time:.3f}s{args_info}"
|
|
65
|
+
)
|
|
66
|
+
return result
|
|
67
|
+
|
|
68
|
+
except Exception as e:
|
|
69
|
+
execution_time = time.time() - start_time
|
|
70
|
+
logger.error(
|
|
71
|
+
f"PERFORMANCE | {function_name} | ERROR | {execution_time:.3f}s | {str(e)}{args_info}"
|
|
72
|
+
)
|
|
73
|
+
raise
|
|
74
|
+
|
|
75
|
+
@functools.wraps(f)
|
|
76
|
+
def sync_wrapper(*args, **kwargs):
|
|
77
|
+
start_time = time.time()
|
|
78
|
+
function_name = f"{f.__module__}.{f.__name__}"
|
|
79
|
+
request_id = get_request_id()
|
|
80
|
+
|
|
81
|
+
# Log function arguments if requested
|
|
82
|
+
args_info = ""
|
|
83
|
+
if include_args:
|
|
84
|
+
args_str = str(args)[:100] if args else ""
|
|
85
|
+
kwargs_str = str(kwargs)[:100] if kwargs else ""
|
|
86
|
+
args_info = f" | Args: {args_str} | Kwargs: {kwargs_str}"
|
|
87
|
+
|
|
88
|
+
try:
|
|
89
|
+
# Execute the sync function
|
|
90
|
+
result = f(*args, **kwargs)
|
|
91
|
+
execution_time = time.time() - start_time
|
|
92
|
+
|
|
93
|
+
# Only log if execution time meets minimum threshold
|
|
94
|
+
if execution_time >= min_duration:
|
|
95
|
+
log_method = getattr(logger, log_level.lower())
|
|
96
|
+
log_method(
|
|
97
|
+
f"PERFORMANCE | {function_name} | SUCCESS | {execution_time:.3f}s{args_info}"
|
|
98
|
+
)
|
|
99
|
+
return result
|
|
100
|
+
|
|
101
|
+
except Exception as e:
|
|
102
|
+
execution_time = time.time() - start_time
|
|
103
|
+
logger.error(
|
|
104
|
+
f"PERFORMANCE | {function_name} | ERROR | {execution_time:.3f}s | {str(e)}{args_info}"
|
|
105
|
+
)
|
|
106
|
+
raise
|
|
107
|
+
|
|
108
|
+
# Return the appropriate wrapper based on function type
|
|
109
|
+
return async_wrapper if asyncio.iscoroutinefunction(f) else sync_wrapper
|
|
110
|
+
|
|
111
|
+
# Handle both @log_performance and @log_performance(...) usage
|
|
112
|
+
if func is None:
|
|
113
|
+
return decorator
|
|
114
|
+
else:
|
|
115
|
+
return decorator(func)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def log_execution_time(name: str, start_time: float, success: bool = True, error: str = None):
|
|
119
|
+
"""
|
|
120
|
+
Manual function to log execution time for code blocks
|
|
121
|
+
|
|
122
|
+
Usage:
|
|
123
|
+
start_time = time.time()
|
|
124
|
+
try:
|
|
125
|
+
# Your code here
|
|
126
|
+
log_execution_time("database_query", start_time, success=True)
|
|
127
|
+
except Exception as e:
|
|
128
|
+
log_execution_time("database_query", start_time, success=False, error=str(e))
|
|
129
|
+
"""
|
|
130
|
+
execution_time = time.time() - start_time
|
|
131
|
+
status = "SUCCESS" if success else "ERROR"
|
|
132
|
+
error_info = f" | {error}" if error else ""
|
|
133
|
+
|
|
134
|
+
if success:
|
|
135
|
+
logger.info(f"PERFORMANCE | {name} | {status} | {execution_time:.3f}s{error_info}")
|
|
136
|
+
else:
|
|
137
|
+
logger.error(f"PERFORMANCE | {name} | {status} | {execution_time:.3f}s{error_info}")
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
# Context manager for measuring code blocks
|
|
141
|
+
class PerformanceTimer:
|
|
142
|
+
"""
|
|
143
|
+
Context manager for measuring execution time of code blocks
|
|
144
|
+
|
|
145
|
+
Usage:
|
|
146
|
+
with PerformanceTimer("database_operation"):
|
|
147
|
+
# Your code here
|
|
148
|
+
pass
|
|
149
|
+
"""
|
|
150
|
+
def __init__(self, name: str, min_duration: float = 0.0):
|
|
151
|
+
self.name = name
|
|
152
|
+
self.min_duration = min_duration
|
|
153
|
+
self.start_time = None
|
|
154
|
+
|
|
155
|
+
def __enter__(self):
|
|
156
|
+
self.start_time = time.time()
|
|
157
|
+
return self
|
|
158
|
+
|
|
159
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
160
|
+
execution_time = time.time() - self.start_time
|
|
161
|
+
if execution_time >= self.min_duration:
|
|
162
|
+
if exc_type is None:
|
|
163
|
+
logger.info(f"PERFORMANCE | {self.name} | SUCCESS | {execution_time:.3f}s")
|
|
164
|
+
else:
|
|
165
|
+
logger.error(f"PERFORMANCE | {self.name} | ERROR | {execution_time:.3f}s | {str(exc_val)}")
|
|
166
|
+
return False # Donโt suppress exceptions
|
|
File without changes
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from contextvars import ContextVar
|
|
2
|
+
import uuid
|
|
3
|
+
|
|
4
|
+
request_id_var: ContextVar[str] = ContextVar("request_id", default="")
|
|
5
|
+
|
|
6
|
+
def set_request_id(req_id: str = None) -> str:
|
|
7
|
+
"""
|
|
8
|
+
Set request ID for current context
|
|
9
|
+
Args:
|
|
10
|
+
req_id: Optional request ID. If None, generates a new one
|
|
11
|
+
Returns:
|
|
12
|
+
The request ID that was set
|
|
13
|
+
"""
|
|
14
|
+
if req_id is None:
|
|
15
|
+
req_id = str(uuid.uuid4())[:8] # Use first 8 chars of UUID
|
|
16
|
+
request_id_var.set(req_id)
|
|
17
|
+
return req_id
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def get_request_id() -> str:
|
|
21
|
+
"""Get current request ID from context"""
|
|
22
|
+
return request_id_var.get("")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def clear_request_id():
|
|
26
|
+
"""Clear the current request ID"""
|
|
27
|
+
request_id_var.set("")
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sherlock-ai
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: A Python package for performance monitoring and logging utilities
|
|
5
|
+
Author-email: Pranaw Mishra <pranawmishra73@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/pranawmishra/sherlock-ai.git
|
|
8
|
+
Project-URL: Repository, https://github.com/pranawmishra/sherlock-ai.git
|
|
9
|
+
Keywords: performance,monitoring,logging,debugging,profiling
|
|
10
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
+
Classifier: Topic :: System :: Monitoring
|
|
21
|
+
Requires-Python: >=3.8
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
|
|
26
|
+
# Sherlock AI
|
|
27
|
+
|
|
28
|
+
A Python package for performance monitoring and logging utilities that helps you track execution times and debug your applications with ease.
|
|
29
|
+
|
|
30
|
+
## Features
|
|
31
|
+
|
|
32
|
+
- ๐ฏ **Performance Decorators**: Easy-to-use decorators for tracking function execution times
|
|
33
|
+
- โฑ๏ธ **Context Managers**: Monitor code block execution with simple context managers
|
|
34
|
+
- ๐ง **Flexible Configuration**: Customizable logging levels, minimum duration thresholds, and argument logging
|
|
35
|
+
- ๐ **Async/Sync Support**: Works seamlessly with both synchronous and asynchronous functions
|
|
36
|
+
- ๐ **Request Tracking**: Built-in request ID tracking for distributed systems
|
|
37
|
+
- ๐ **Zero Dependencies**: Lightweight with minimal external dependencies
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pip install sherlock-ai
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Quick Start
|
|
46
|
+
|
|
47
|
+
### Basic Setup
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
from sherlock_ai.logging_config import setup_logging
|
|
51
|
+
from sherlock_ai.performance import log_performance
|
|
52
|
+
|
|
53
|
+
# Initialize logging (call once at application startup)
|
|
54
|
+
setup_logging()
|
|
55
|
+
|
|
56
|
+
@log_performance
|
|
57
|
+
def my_function():
|
|
58
|
+
# Your code here
|
|
59
|
+
time.sleep(1)
|
|
60
|
+
return "result"
|
|
61
|
+
|
|
62
|
+
# This will log: PERFORMANCE | my_module.my_function | SUCCESS | 1.003s
|
|
63
|
+
result = my_function()
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Advanced Configuration
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
@log_performance(min_duration=0.1, include_args=True, log_level="DEBUG")
|
|
70
|
+
def slow_database_query(user_id, limit=10):
|
|
71
|
+
# Only logs if execution time >= 0.1 seconds
|
|
72
|
+
# Includes function arguments in the log
|
|
73
|
+
pass
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Context Manager for Code Blocks
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
from sherlock_ai.performance import PerformanceTimer
|
|
80
|
+
|
|
81
|
+
with PerformanceTimer("database_operation"):
|
|
82
|
+
# Your code block here
|
|
83
|
+
result = database.query("SELECT * FROM users")
|
|
84
|
+
|
|
85
|
+
# Logs: PERFORMANCE | database_operation | SUCCESS | 0.234s
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Async Function Support
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
@log_performance
|
|
92
|
+
async def async_api_call():
|
|
93
|
+
async with httpx.AsyncClient() as client:
|
|
94
|
+
response = await client.get("https://api.example.com")
|
|
95
|
+
return response.json()
|
|
96
|
+
|
|
97
|
+
# Works automatically with async functions
|
|
98
|
+
result = await async_api_call()
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Manual Time Logging
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
from sherlock_ai.performance import log_execution_time
|
|
105
|
+
import time
|
|
106
|
+
|
|
107
|
+
start_time = time.time()
|
|
108
|
+
try:
|
|
109
|
+
# Your code here
|
|
110
|
+
result = complex_operation()
|
|
111
|
+
log_execution_time("complex_operation", start_time, success=True)
|
|
112
|
+
except Exception as e:
|
|
113
|
+
log_execution_time("complex_operation", start_time, success=False, error=str(e))
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## API Reference
|
|
117
|
+
|
|
118
|
+
### `@log_performance` Decorator
|
|
119
|
+
|
|
120
|
+
Parameters:
|
|
121
|
+
- `min_duration` (float): Only log if execution time >= this value in seconds (default: 0.0)
|
|
122
|
+
- `include_args` (bool): Whether to include function arguments in the log (default: False)
|
|
123
|
+
- `log_level` (str): Log level to use - INFO, DEBUG, WARNING, etc. (default: "INFO")
|
|
124
|
+
|
|
125
|
+
### `PerformanceTimer` Context Manager
|
|
126
|
+
|
|
127
|
+
Parameters:
|
|
128
|
+
- `name` (str): Name identifier for the operation
|
|
129
|
+
- `min_duration` (float): Only log if execution time >= this value in seconds (default: 0.0)
|
|
130
|
+
|
|
131
|
+
### `log_execution_time` Function
|
|
132
|
+
|
|
133
|
+
Parameters:
|
|
134
|
+
- `name` (str): Name identifier for the operation
|
|
135
|
+
- `start_time` (float): Start time from `time.time()`
|
|
136
|
+
- `success` (bool): Whether the operation succeeded (default: True)
|
|
137
|
+
- `error` (str): Error message if operation failed (default: None)
|
|
138
|
+
|
|
139
|
+
## Configuration
|
|
140
|
+
|
|
141
|
+
### Logging Setup
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
from sherlock_ai.logging_config import setup_logging, get_logger
|
|
145
|
+
|
|
146
|
+
# Initialize logging (call once at application startup)
|
|
147
|
+
setup_logging()
|
|
148
|
+
|
|
149
|
+
# Get a logger for your module
|
|
150
|
+
logger = get_logger(__name__)
|
|
151
|
+
|
|
152
|
+
# Use the logger
|
|
153
|
+
logger.info("Application started")
|
|
154
|
+
logger.error("Something went wrong")
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**Log Files Created:**
|
|
158
|
+
When you call `setup_logging()`, it automatically creates a `logs/` directory with these files:
|
|
159
|
+
- `app.log` - All INFO+ level logs
|
|
160
|
+
- `errors.log` - Only ERROR+ level logs
|
|
161
|
+
- `api.log` - API-related logs
|
|
162
|
+
- `database.log` - Database operation logs
|
|
163
|
+
- `services.log` - Service operation logs
|
|
164
|
+
- `performance.log` - Performance monitoring logs
|
|
165
|
+
|
|
166
|
+
### Request ID Tracking
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
from sherlock_ai.utils.helper import get_request_id
|
|
170
|
+
|
|
171
|
+
# Get current request ID for distributed tracing
|
|
172
|
+
request_id = get_request_id()
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Complete Application Example
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
from sherlock_ai.logging_config import setup_logging, get_logger
|
|
179
|
+
from sherlock_ai.performance import log_performance, PerformanceTimer
|
|
180
|
+
|
|
181
|
+
# Initialize logging first
|
|
182
|
+
setup_logging()
|
|
183
|
+
logger = get_logger(__name__)
|
|
184
|
+
|
|
185
|
+
@log_performance
|
|
186
|
+
def main():
|
|
187
|
+
logger.info("Application starting")
|
|
188
|
+
|
|
189
|
+
with PerformanceTimer("initialization"):
|
|
190
|
+
# Your initialization code
|
|
191
|
+
pass
|
|
192
|
+
|
|
193
|
+
logger.info("Application ready")
|
|
194
|
+
|
|
195
|
+
if __name__ == "__main__":
|
|
196
|
+
main()
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Log Output Format
|
|
200
|
+
|
|
201
|
+
The package produces structured log messages in the following format:
|
|
202
|
+
|
|
203
|
+
```
|
|
204
|
+
PERFORMANCE | {function_name} | {STATUS} | {execution_time}s | {additional_info}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Examples:
|
|
208
|
+
```
|
|
209
|
+
PERFORMANCE | my_module.my_function | SUCCESS | 0.123s
|
|
210
|
+
PERFORMANCE | api_call | ERROR | 2.456s | Connection timeout
|
|
211
|
+
PERFORMANCE | database_query | SUCCESS | 0.089s | Args: ('user123',) | Kwargs: {'limit': 10}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## Use Cases
|
|
215
|
+
|
|
216
|
+
- **API Performance Monitoring**: Track response times for your web APIs
|
|
217
|
+
- **Database Query Optimization**: Monitor slow database operations
|
|
218
|
+
- **Microservices Debugging**: Trace execution times across service boundaries
|
|
219
|
+
- **Algorithm Benchmarking**: Compare performance of different implementations
|
|
220
|
+
- **Production Monitoring**: Get insights into your application's performance characteristics
|
|
221
|
+
|
|
222
|
+
## Requirements
|
|
223
|
+
|
|
224
|
+
- Python >= 3.13
|
|
225
|
+
- Standard library only (no external dependencies)
|
|
226
|
+
|
|
227
|
+
## License
|
|
228
|
+
|
|
229
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
230
|
+
|
|
231
|
+
## Contributing
|
|
232
|
+
|
|
233
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
234
|
+
|
|
235
|
+
## Links
|
|
236
|
+
|
|
237
|
+
- **Homepage**: [https://github.com/pranawmishra/sherlock-ai](https://github.com/pranawmishra/sherlock-ai)
|
|
238
|
+
- **Repository**: [https://github.com/pranawmishra/sherlock-ai](https://github.com/pranawmishra/sherlock-ai)
|
|
239
|
+
- **Issues**: [https://github.com/pranawmishra/sherlock-ai/issues](https://github.com/pranawmishra/sherlock-ai/issues)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
src/sherlock_ai/__init__.py
|
|
5
|
+
src/sherlock_ai/logging_config.py
|
|
6
|
+
src/sherlock_ai/performance.py
|
|
7
|
+
src/sherlock_ai.egg-info/PKG-INFO
|
|
8
|
+
src/sherlock_ai.egg-info/SOURCES.txt
|
|
9
|
+
src/sherlock_ai.egg-info/dependency_links.txt
|
|
10
|
+
src/sherlock_ai.egg-info/top_level.txt
|
|
11
|
+
src/sherlock_ai/utils/__init__.py
|
|
12
|
+
src/sherlock_ai/utils/helper.py
|
|
13
|
+
tests/test_local.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
sherlock_ai
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# test_local.py
|
|
2
|
+
import time
|
|
3
|
+
import asyncio
|
|
4
|
+
from sherlock_ai.performance import log_performance, PerformanceTimer, log_execution_time
|
|
5
|
+
from sherlock_ai.logging_config import setup_logging, get_logger
|
|
6
|
+
|
|
7
|
+
setup_logging()
|
|
8
|
+
|
|
9
|
+
logger = get_logger(__name__)
|
|
10
|
+
|
|
11
|
+
# Test 1: Basic decorator
|
|
12
|
+
@log_performance
|
|
13
|
+
def test_sync_function():
|
|
14
|
+
"""Test synchronous function"""
|
|
15
|
+
time.sleep(0.1)
|
|
16
|
+
return "sync result"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# Test 2: Decorator with options
|
|
20
|
+
@log_performance(min_duration=0.05, include_args=True, log_level="DEBUG")
|
|
21
|
+
def test_function_with_args(name, count=5):
|
|
22
|
+
"""Test function with arguments"""
|
|
23
|
+
time.sleep(0.1)
|
|
24
|
+
return f"Processed {count} items for {name}"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
# Test 3: Async function
|
|
28
|
+
@log_performance
|
|
29
|
+
async def test_async_function():
|
|
30
|
+
"""Test asynchronous function"""
|
|
31
|
+
await asyncio.sleep(0.1)
|
|
32
|
+
return "async result"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# Test 4: Context manager
|
|
36
|
+
def test_context_manager():
|
|
37
|
+
"""Test PerformanceTimer context manager"""
|
|
38
|
+
with PerformanceTimer("test_operation"):
|
|
39
|
+
time.sleep(0.1)
|
|
40
|
+
print("Operation completed")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
# Test 5: Manual logging
|
|
44
|
+
def test_manual_logging():
|
|
45
|
+
"""Test manual execution time logging"""
|
|
46
|
+
start_time = time.time()
|
|
47
|
+
try:
|
|
48
|
+
time.sleep(0.1)
|
|
49
|
+
log_execution_time("manual_test", start_time, success=True)
|
|
50
|
+
except Exception as e:
|
|
51
|
+
log_execution_time("manual_test", start_time, success=False, error=str(e))
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
async def test_main():
|
|
55
|
+
"""Run all tests"""
|
|
56
|
+
logger.info("๐งช Testing sherlock-ai package locally...")
|
|
57
|
+
logger.info("=" * 50)
|
|
58
|
+
|
|
59
|
+
# Test imports
|
|
60
|
+
logger.info("โ
Import test passed")
|
|
61
|
+
|
|
62
|
+
# Test sync function
|
|
63
|
+
logger.info("\n๐ Testing sync function...")
|
|
64
|
+
result1 = test_sync_function()
|
|
65
|
+
logger.info(f"Result: {result1}")
|
|
66
|
+
|
|
67
|
+
# Test function with args
|
|
68
|
+
logger.info("\n๐ Testing function with arguments...")
|
|
69
|
+
result2 = test_function_with_args("Alice", count=10)
|
|
70
|
+
logger.info(f"Result: {result2}")
|
|
71
|
+
|
|
72
|
+
# Test async function
|
|
73
|
+
logger.info("\n๐ Testing async function...")
|
|
74
|
+
result3 = await test_async_function()
|
|
75
|
+
logger.info(f"Result: {result3}")
|
|
76
|
+
|
|
77
|
+
# Test context manager
|
|
78
|
+
# logger.info("\n๐ Testing context manager...")
|
|
79
|
+
# test_context_manager()
|
|
80
|
+
|
|
81
|
+
# Test manual logging
|
|
82
|
+
# logger.info("\n๐ Testing manual logging...")
|
|
83
|
+
# test_manual_logging()
|
|
84
|
+
|
|
85
|
+
logger.info("\nโ
All tests completed!")
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
if __name__ == "__main__":
|
|
89
|
+
asyncio.run(test_main())
|