ganicas-package 0.1.3__tar.gz → 0.2.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.
- ganicas_package-0.2.0/PKG-INFO +443 -0
- ganicas_package-0.2.0/README.md +418 -0
- {ganicas_package-0.1.3 → ganicas_package-0.2.0}/ganicas_utils/logging/configuration.py +1 -1
- {ganicas_package-0.1.3 → ganicas_package-0.2.0}/ganicas_utils/logging/logger.py +4 -2
- ganicas_package-0.2.0/ganicas_utils/logging/middlewares.py +248 -0
- {ganicas_package-0.1.3 → ganicas_package-0.2.0}/pyproject.toml +7 -7
- ganicas_package-0.1.3/PKG-INFO +0 -228
- ganicas_package-0.1.3/README.md +0 -203
- ganicas_package-0.1.3/ganicas_utils/logging/__pycache__/__init__.cpython-312.pyc +0 -0
- ganicas_package-0.1.3/ganicas_utils/logging/__pycache__/configuration.cpython-312.pyc +0 -0
- ganicas_package-0.1.3/ganicas_utils/logging/__pycache__/formatter.cpython-312.pyc +0 -0
- ganicas_package-0.1.3/ganicas_utils/logging/__pycache__/logger.cpython-312.pyc +0 -0
- ganicas_package-0.1.3/ganicas_utils/logging/__pycache__/middlewares.cpython-312.pyc +0 -0
- ganicas_package-0.1.3/ganicas_utils/logging/middlewares.py +0 -48
- {ganicas_package-0.1.3 → ganicas_package-0.2.0}/ganicas_utils/__init__.py +0 -0
- {ganicas_package-0.1.3 → ganicas_package-0.2.0}/ganicas_utils/config.py +0 -0
- {ganicas_package-0.1.3 → ganicas_package-0.2.0}/ganicas_utils/logging/__init__.py +0 -0
- {ganicas_package-0.1.3 → ganicas_package-0.2.0}/ganicas_utils/logging/formatter.py +0 -0
- {ganicas_package-0.1.3 → ganicas_package-0.2.0}/ganicas_utils/logging/utils.py +0 -0
@@ -0,0 +1,443 @@
|
|
1
|
+
Metadata-Version: 2.1
|
2
|
+
Name: ganicas-package
|
3
|
+
Version: 0.2.0
|
4
|
+
Summary: Ganicas internal Python package for structured logging and utilities.
|
5
|
+
Keywords: logging,utilities,internal-package,structlog,middleware,fastapi,flask
|
6
|
+
Author: Ganicas
|
7
|
+
Requires-Python: >=3.11,<4.0
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
9
|
+
Classifier: Framework :: FastAPI
|
10
|
+
Classifier: Framework :: Flask
|
11
|
+
Classifier: Intended Audience :: Developers
|
12
|
+
Classifier: License :: Other/Proprietary License
|
13
|
+
Classifier: Natural Language :: English
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
17
|
+
Classifier: Topic :: System :: Logging
|
18
|
+
Requires-Dist: fastapi (>=0.114.2,<0.116.0)
|
19
|
+
Requires-Dist: flask (>=2.2.0,<3.0.0)
|
20
|
+
Requires-Dist: httpx (>=0.28.1,<0.29.0)
|
21
|
+
Requires-Dist: python-json-logger (>=3.2.1,<4.0.0)
|
22
|
+
Requires-Dist: structlog (>=24.4.0,<25.0.0)
|
23
|
+
Description-Content-Type: text/markdown
|
24
|
+
|
25
|
+
# Ganicas Utils
|
26
|
+
|
27
|
+
[](https://www.python.org/downloads/)
|
28
|
+
[](LICENSE)
|
29
|
+
[](https://github.com/ganicas/ganicas_utils)
|
30
|
+
|
31
|
+
**Ganicas Utils** is an internal Python package providing structured logging utilities and middleware for Flask and FastAPI applications. Built on top of [structlog](https://www.structlog.org/), it enables production-ready, context-aware logging with minimal configuration.
|
32
|
+
|
33
|
+
---
|
34
|
+
|
35
|
+
## 📋 Table of Contents
|
36
|
+
|
37
|
+
- [Features](#-features)
|
38
|
+
- [Installation](#-installation)
|
39
|
+
- [Quick Start](#-quick-start)
|
40
|
+
- [Structured Logging](#-structured-logging)
|
41
|
+
- [Basic Configuration](#basic-configuration)
|
42
|
+
- [Production Configuration](#production-configuration)
|
43
|
+
- [Middleware](#-middleware)
|
44
|
+
- [Flask Middleware](#flask-middleware)
|
45
|
+
- [FastAPI Middleware](#fastapi-middleware)
|
46
|
+
- [Request Logging Middleware](#request-logging-middleware)
|
47
|
+
- [Why Structured Logging?](#-why-structured-logging)
|
48
|
+
- [Development](#-development)
|
49
|
+
- [License](#-license)
|
50
|
+
|
51
|
+
---
|
52
|
+
|
53
|
+
## ✨ Features
|
54
|
+
|
55
|
+
- **Structured Logging**: JSON-formatted logs for easy parsing by log aggregation tools (ELK, Datadog, Grafana Loki)
|
56
|
+
- **Context Management**: Automatic request context binding (request_id, IP, user_agent, etc.)
|
57
|
+
- **Flask & FastAPI Support**: Ready-to-use middleware for both frameworks
|
58
|
+
- **Advanced Request Logging**: Comprehensive ASGI middleware with:
|
59
|
+
- Automatic request/response logging
|
60
|
+
- Sensitive header sanitization
|
61
|
+
- Slow request detection
|
62
|
+
- Sampling for high-traffic endpoints
|
63
|
+
- Exception tracking with full context
|
64
|
+
- Distributed tracing support (traceparent header)
|
65
|
+
- **Production Ready**: Battle-tested with 99% code coverage
|
66
|
+
|
67
|
+
---
|
68
|
+
|
69
|
+
## 📦 Installation
|
70
|
+
|
71
|
+
```bash
|
72
|
+
pip install ganicas-package
|
73
|
+
```
|
74
|
+
|
75
|
+
Or with Poetry:
|
76
|
+
|
77
|
+
```bash
|
78
|
+
poetry add ganicas-package
|
79
|
+
```
|
80
|
+
|
81
|
+
---
|
82
|
+
|
83
|
+
## 🚀 Quick Start
|
84
|
+
|
85
|
+
### Basic Configuration
|
86
|
+
|
87
|
+
Replace `logger = logging.getLogger(__name__)` with `logger = structlog.get_logger(__name__)`:
|
88
|
+
|
89
|
+
```python
|
90
|
+
from ganicas_utils.logging import LoggingConfigurator
|
91
|
+
from ganicas_utils.config import Config
|
92
|
+
import structlog
|
93
|
+
|
94
|
+
config = Config()
|
95
|
+
|
96
|
+
LoggingConfigurator(
|
97
|
+
service_name=config.APP_NAME,
|
98
|
+
log_level='INFO',
|
99
|
+
setup_logging_dict=True
|
100
|
+
).configure_structlog(
|
101
|
+
formatter='plain_console',
|
102
|
+
formatter_std_lib='plain_console'
|
103
|
+
)
|
104
|
+
|
105
|
+
logger = structlog.get_logger(__name__)
|
106
|
+
logger.info("Application started", version="1.0.0", environment="production")
|
107
|
+
```
|
108
|
+
|
109
|
+

|
110
|
+
|
111
|
+
---
|
112
|
+
|
113
|
+
## 📊 Structured Logging
|
114
|
+
|
115
|
+
### Production Configuration
|
116
|
+
|
117
|
+
For production environments, use JSON formatting for machine-readable logs:
|
118
|
+
|
119
|
+
```python
|
120
|
+
from ganicas_utils.logging import LoggingConfigurator
|
121
|
+
from ganicas_utils.config import Config
|
122
|
+
import structlog
|
123
|
+
|
124
|
+
config = Config()
|
125
|
+
|
126
|
+
LoggingConfigurator(
|
127
|
+
service_name=config.APP_NAME,
|
128
|
+
log_level='INFO',
|
129
|
+
setup_logging_dict=True
|
130
|
+
).configure_structlog(
|
131
|
+
formatter='json_formatter',
|
132
|
+
formatter_std_lib='json_formatter'
|
133
|
+
)
|
134
|
+
|
135
|
+
logger = structlog.get_logger(__name__)
|
136
|
+
logger.info("User login", user_id=12345, ip_address="192.168.1.1")
|
137
|
+
logger.warning("High memory usage", memory_percent=85.5, threshold=80)
|
138
|
+
logger.error("Database connection failed", db_host="localhost", error_code="CONN_REFUSED")
|
139
|
+
|
140
|
+
try:
|
141
|
+
result = 1 / 0
|
142
|
+
except ZeroDivisionError:
|
143
|
+
logger.exception("Division by zero error", operation="calculate_ratio")
|
144
|
+
```
|
145
|
+
|
146
|
+

|
147
|
+
|
148
|
+
|
149
|
+
---
|
150
|
+
|
151
|
+
## 🔧 Middleware
|
152
|
+
|
153
|
+
### Flask Middleware
|
154
|
+
|
155
|
+
The `FlaskRequestContextMiddleware` automatically adds request context to all logs:
|
156
|
+
|
157
|
+
```python
|
158
|
+
import uuid
|
159
|
+
from flask import Flask
|
160
|
+
from ganicas_utils.logging import LoggingConfigurator
|
161
|
+
from ganicas_utils.logging.middlewares import FlaskRequestContextMiddleware
|
162
|
+
from ganicas_utils.config import Config
|
163
|
+
import structlog
|
164
|
+
|
165
|
+
config = Config()
|
166
|
+
|
167
|
+
LoggingConfigurator(
|
168
|
+
service_name=config.APP_NAME,
|
169
|
+
log_level="INFO",
|
170
|
+
setup_logging_dict=True,
|
171
|
+
).configure_structlog(formatter='json_formatter', formatter_std_lib='json_formatter')
|
172
|
+
|
173
|
+
logger = structlog.get_logger(__name__)
|
174
|
+
|
175
|
+
app = Flask(__name__)
|
176
|
+
app.wsgi_app = FlaskRequestContextMiddleware(app.wsgi_app)
|
177
|
+
|
178
|
+
@app.route("/")
|
179
|
+
def home():
|
180
|
+
logger.info("Processing request") # Automatically includes request_id, method, path
|
181
|
+
return "Hello, World!"
|
182
|
+
|
183
|
+
if __name__ == "__main__":
|
184
|
+
app.run()
|
185
|
+
```
|
186
|
+
|
187
|
+

|
188
|
+
|
189
|
+
**Automatic context injection:**
|
190
|
+
- `request_id` - Unique identifier for each request
|
191
|
+
- `request_method` - HTTP method (GET, POST, etc.)
|
192
|
+
- `request_path` - Request URL path
|
193
|
+
|
194
|
+
---
|
195
|
+
|
196
|
+
### FastAPI Middleware
|
197
|
+
|
198
|
+
#### Basic Context Middleware
|
199
|
+
|
200
|
+
For simple request context binding, use `FastAPIRequestContextMiddleware`:
|
201
|
+
|
202
|
+
```python
|
203
|
+
from fastapi import FastAPI
|
204
|
+
from ganicas_utils.logging import LoggingConfigurator
|
205
|
+
from ganicas_utils.logging.middlewares import FastAPIRequestContextMiddleware
|
206
|
+
from ganicas_utils.config import Config
|
207
|
+
import structlog
|
208
|
+
|
209
|
+
config = Config()
|
210
|
+
|
211
|
+
LoggingConfigurator(
|
212
|
+
service_name=config.APP_NAME,
|
213
|
+
log_level="INFO",
|
214
|
+
setup_logging_dict=True,
|
215
|
+
).configure_structlog(formatter='json_formatter', formatter_std_lib='json_formatter')
|
216
|
+
|
217
|
+
logger = structlog.get_logger(__name__)
|
218
|
+
app = FastAPI()
|
219
|
+
app.add_middleware(FastAPIRequestContextMiddleware)
|
220
|
+
|
221
|
+
@app.get("/")
|
222
|
+
async def root():
|
223
|
+
logger.info("Processing request") # Automatically includes request context
|
224
|
+
return {"message": "Hello World"}
|
225
|
+
```
|
226
|
+
|
227
|
+

|
228
|
+
|
229
|
+
---
|
230
|
+
|
231
|
+
### Request Logging Middleware
|
232
|
+
|
233
|
+
For production-grade request/response logging with advanced features, use `RequestLoggingMiddleware`:
|
234
|
+
|
235
|
+
```python
|
236
|
+
from fastapi import FastAPI
|
237
|
+
from ganicas_utils.logging import LoggingConfigurator
|
238
|
+
from ganicas_utils.logging.middlewares import RequestLoggingMiddleware
|
239
|
+
import structlog
|
240
|
+
|
241
|
+
LoggingConfigurator(
|
242
|
+
service_name="my-api",
|
243
|
+
log_level="INFO",
|
244
|
+
setup_logging_dict=True,
|
245
|
+
).configure_structlog(formatter='json_formatter', formatter_std_lib='json_formatter')
|
246
|
+
|
247
|
+
app = FastAPI()
|
248
|
+
|
249
|
+
# Add comprehensive request logging
|
250
|
+
app.add_middleware(
|
251
|
+
RequestLoggingMiddleware,
|
252
|
+
slow_request_threshold_ms=1000, # Warn on requests > 1s
|
253
|
+
propagate_request_id=True, # Add request_id to response headers
|
254
|
+
skip_paths={"/healthz", "/metrics"}, # Don't log health checks
|
255
|
+
sample_2xx_rate=0.1, # Sample 10% of successful requests
|
256
|
+
)
|
257
|
+
|
258
|
+
@app.get("/api/users/{user_id}")
|
259
|
+
async def get_user(user_id: int):
|
260
|
+
return {"user_id": user_id, "name": "John Doe"}
|
261
|
+
```
|
262
|
+
|
263
|
+
#### Features
|
264
|
+
|
265
|
+
**Automatic Logging:**
|
266
|
+
- `request.start` - Logs when request begins
|
267
|
+
- `request.end` - Logs when request completes (with status, duration, size)
|
268
|
+
- `request.exception` - Logs unhandled exceptions with full traceback
|
269
|
+
|
270
|
+
**Logged Information:**
|
271
|
+
- Request: method, path, query params, client IP, user agent, content type/length
|
272
|
+
- Response: status code, size, content type, duration
|
273
|
+
- Headers: Sanitized request/response headers (for 4xx/5xx errors)
|
274
|
+
- Performance: Request duration, slow request detection
|
275
|
+
|
276
|
+
**Security:**
|
277
|
+
- Automatic sanitization of sensitive headers (`Authorization`, `Cookie`, `X-API-Key`)
|
278
|
+
- Authorization header preserves scheme: `Bearer ***` instead of exposing tokens
|
279
|
+
- No request/response body logging (only sizes)
|
280
|
+
|
281
|
+
**Performance Optimization:**
|
282
|
+
- Skip logging for health checks and metrics endpoints
|
283
|
+
- Sample successful requests to reduce log volume
|
284
|
+
- Skip OPTIONS requests
|
285
|
+
- Configurable path prefixes to skip
|
286
|
+
|
287
|
+
**Distributed Tracing:**
|
288
|
+
- Supports W3C `traceparent` header
|
289
|
+
- Falls back to `x-request-id` or `x-amzn-trace-id`
|
290
|
+
- Propagates request_id to response headers
|
291
|
+
|
292
|
+
#### Configuration Options
|
293
|
+
|
294
|
+
| Parameter | Type | Default | Description |
|
295
|
+
|-----------|------|---------|-------------|
|
296
|
+
| `logger` | `structlog.BoundLoggerBase` | `structlog.get_logger("http")` | Custom logger instance |
|
297
|
+
| `slow_request_threshold_ms` | `int` | `None` | Threshold in ms to flag slow requests |
|
298
|
+
| `propagate_request_id` | `bool` | `True` | Add `x-request-id` to response headers |
|
299
|
+
| `skip_paths` | `set[str]` | `{"/healthz", "/metrics"}` | Exact paths to skip logging |
|
300
|
+
| `skip_prefixes` | `tuple[str, ...]` | `("/metrics",)` | Path prefixes to skip logging |
|
301
|
+
| `sample_2xx_rate` | `float` | `None` | Sample rate for 2xx/3xx responses (0.0-1.0) |
|
302
|
+
|
303
|
+
#### Example Logs
|
304
|
+
|
305
|
+
**Successful Request:**
|
306
|
+
```json
|
307
|
+
{
|
308
|
+
"event": "request.end",
|
309
|
+
"request_id": "550e8400-e29b-41d4-a716-446655440000",
|
310
|
+
"method": "GET",
|
311
|
+
"path": "/api/users/123",
|
312
|
+
"status_code": 200,
|
313
|
+
"duration_ms": 45,
|
314
|
+
"response_size": 256,
|
315
|
+
"client_ip": "192.168.1.100",
|
316
|
+
"user_agent": "Mozilla/5.0...",
|
317
|
+
"level": "info"
|
318
|
+
}
|
319
|
+
```
|
320
|
+
|
321
|
+
**Slow Request Warning:**
|
322
|
+
```json
|
323
|
+
{
|
324
|
+
"event": "request.end",
|
325
|
+
"request_id": "550e8400-e29b-41d4-a716-446655440001",
|
326
|
+
"method": "POST",
|
327
|
+
"path": "/api/process",
|
328
|
+
"status_code": 200,
|
329
|
+
"duration_ms": 1523,
|
330
|
+
"slow_request": true,
|
331
|
+
"slow_threshold_ms": 1000,
|
332
|
+
"level": "warning"
|
333
|
+
}
|
334
|
+
```
|
335
|
+
|
336
|
+
**Error with Sanitized Headers:**
|
337
|
+
```json
|
338
|
+
{
|
339
|
+
"event": "request.end",
|
340
|
+
"request_id": "550e8400-e29b-41d4-a716-446655440002",
|
341
|
+
"method": "POST",
|
342
|
+
"path": "/api/login",
|
343
|
+
"status_code": 401,
|
344
|
+
"duration_ms": 12,
|
345
|
+
"request_headers": {
|
346
|
+
"authorization": "Bearer ***",
|
347
|
+
"content-type": "application/json"
|
348
|
+
},
|
349
|
+
"level": "warning"
|
350
|
+
}
|
351
|
+
```
|
352
|
+
|
353
|
+
---
|
354
|
+
|
355
|
+
## 🎯 Why Structured Logging?
|
356
|
+
|
357
|
+
**Traditional logging challenges:**
|
358
|
+
- Plain text logs are hard to parse programmatically
|
359
|
+
- Difficult to filter and search in log aggregation tools
|
360
|
+
- Missing context makes debugging distributed systems challenging
|
361
|
+
|
362
|
+
**Structured logging benefits:**
|
363
|
+
- **Machine-readable**: JSON format for easy parsing by ELK, Datadog, Grafana Loki
|
364
|
+
- **Rich context**: Automatic correlation with request_id, user_id, transaction_id
|
365
|
+
- **Better filtering**: Query logs by any field (status_code, duration, user_id, etc.)
|
366
|
+
- **Observability**: Enhanced monitoring and alerting capabilities
|
367
|
+
- **Debugging**: Trace requests across microservices with distributed tracing support
|
368
|
+
|
369
|
+
**This package uses [structlog](https://www.structlog.org/)** - a powerful library that enhances Python's standard logging with better context management and flexible log formatting.
|
370
|
+
|
371
|
+
|
372
|
+
---
|
373
|
+
|
374
|
+
## 🛠️ Development
|
375
|
+
|
376
|
+
### Prerequisites
|
377
|
+
|
378
|
+
Install [Poetry](https://python-poetry.org/docs/#installation) for dependency management:
|
379
|
+
|
380
|
+
```bash
|
381
|
+
curl -sSL https://install.python-poetry.org | python3 -
|
382
|
+
```
|
383
|
+
|
384
|
+
### Setup
|
385
|
+
|
386
|
+
```bash
|
387
|
+
# Install dependencies
|
388
|
+
poetry install --with dev
|
389
|
+
|
390
|
+
# Run tests with coverage
|
391
|
+
poetry run pytest -v --cov=ganicas_utils
|
392
|
+
|
393
|
+
# Run tests with detailed output
|
394
|
+
poetry run pytest -rs --cov=ganicas_utils -s
|
395
|
+
|
396
|
+
# Run pre-commit hooks
|
397
|
+
poetry run pre-commit run --all-files
|
398
|
+
```
|
399
|
+
|
400
|
+
### Running Tests
|
401
|
+
|
402
|
+
```bash
|
403
|
+
# Run all tests
|
404
|
+
poetry run pytest
|
405
|
+
|
406
|
+
# Run specific test file
|
407
|
+
poetry run pytest tests/test_request_logging_middleware.py
|
408
|
+
|
409
|
+
# Run with coverage report
|
410
|
+
poetry run pytest --cov=ganicas_utils --cov-report=html
|
411
|
+
```
|
412
|
+
|
413
|
+
### Code Quality
|
414
|
+
|
415
|
+
This project uses:
|
416
|
+
- **pytest** for testing (99% coverage)
|
417
|
+
- **ruff** for linting and formatting
|
418
|
+
- **pre-commit** for automated checks
|
419
|
+
|
420
|
+
---
|
421
|
+
|
422
|
+
## 📄 License
|
423
|
+
|
424
|
+
Proprietary - Internal use only for Ganicas projects.
|
425
|
+
|
426
|
+
---
|
427
|
+
|
428
|
+
## 🤝 Contributing
|
429
|
+
|
430
|
+
This is an internal package. For questions or contributions, please contact the Ganicas development team.
|
431
|
+
|
432
|
+
---
|
433
|
+
|
434
|
+
## 📚 Additional Resources
|
435
|
+
|
436
|
+
- [structlog Documentation](https://www.structlog.org/en/stable/)
|
437
|
+
- [FastAPI Middleware Guide](https://fastapi.tiangolo.com/tutorial/middleware/)
|
438
|
+
- [Flask Middleware Guide](https://flask.palletsprojects.com/en/latest/api/#flask.Flask.wsgi_app)
|
439
|
+
|
440
|
+
---
|
441
|
+
|
442
|
+
**Made with ❤️ by Ganicas Team**
|
443
|
+
|