ganicas-package 0.1.4__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.
@@ -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
+ [![Python Version](https://img.shields.io/badge/python-3.11%2B-blue.svg)](https://www.python.org/downloads/)
28
+ [![License](https://img.shields.io/badge/license-Proprietary-red.svg)](LICENSE)
29
+ [![Code Coverage](https://img.shields.io/badge/coverage-99%25-brightgreen.svg)](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
+ ![basic example](images/plain_console_logger.png)
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
+ ![logger with different keys](images/json_logger.png)
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
+ ![logger with context flask](images/flask_logger_with_context.png)
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
+ ![logger with context fastapi](images/fastapi_logger_with_context.png)
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
+