Ganicas internal Python package for structured logging and utilities.
Project description
Ganicas Utils
Ganicas Utils is an internal Python package providing structured logging utilities and middleware for Flask and FastAPI applications. Built on top of structlog, it enables production-ready, context-aware logging with minimal configuration.
📋 Table of Contents
- Features
- Installation
- Quick Start
- Structured Logging
- Middleware
- Why Structured Logging?
- Development
- License
✨ Features
- Structured Logging: JSON-formatted logs for easy parsing by log aggregation tools (ELK, Datadog, Grafana Loki)
- Context Management: Automatic request context binding (request_id, IP, user_agent, etc.)
- Flask & FastAPI Support: Ready-to-use middleware for both frameworks
- Advanced Request Logging: Comprehensive ASGI middleware with:
- Automatic request/response logging
- Sensitive header sanitization
- Slow request detection
- Sampling for high-traffic endpoints
- Exception tracking with full context
- Distributed tracing support (traceparent header)
- Production Ready: Battle-tested with 99% code coverage
📦 Installation
pip install ganicas-package
Or with Poetry:
poetry add ganicas-package
🚀 Quick Start
Basic Configuration
Replace logger = logging.getLogger(__name__) with logger = structlog.get_logger(__name__):
from ganicas_utils.logging import LoggingConfigurator
from ganicas_utils.config import Config
import structlog
config = Config()
LoggingConfigurator(
service_name=config.APP_NAME,
log_level='INFO',
setup_logging_dict=True
).configure_structlog(
formatter='plain_console',
formatter_std_lib='plain_console'
)
logger = structlog.get_logger(__name__)
logger.info("Application started", version="1.0.0", environment="production")
📊 Structured Logging
Production Configuration
For production environments, use JSON formatting for machine-readable logs:
from ganicas_utils.logging import LoggingConfigurator
from ganicas_utils.config import Config
import structlog
config = Config()
LoggingConfigurator(
service_name=config.APP_NAME,
log_level='INFO',
setup_logging_dict=True
).configure_structlog(
formatter='json_formatter',
formatter_std_lib='json_formatter'
)
logger = structlog.get_logger(__name__)
logger.info("User login", user_id=12345, ip_address="192.168.1.1")
logger.warning("High memory usage", memory_percent=85.5, threshold=80)
logger.error("Database connection failed", db_host="localhost", error_code="CONN_REFUSED")
try:
result = 1 / 0
except ZeroDivisionError:
logger.exception("Division by zero error", operation="calculate_ratio")
🔧 Middleware
Flask Middleware
The FlaskRequestContextMiddleware automatically adds request context to all logs:
import uuid
from flask import Flask
from ganicas_utils.logging import LoggingConfigurator
from ganicas_utils.logging.middlewares import FlaskRequestContextMiddleware
from ganicas_utils.config import Config
import structlog
config = Config()
LoggingConfigurator(
service_name=config.APP_NAME,
log_level="INFO",
setup_logging_dict=True,
).configure_structlog(formatter='json_formatter', formatter_std_lib='json_formatter')
logger = structlog.get_logger(__name__)
app = Flask(__name__)
app.wsgi_app = FlaskRequestContextMiddleware(app.wsgi_app)
@app.route("/")
def home():
logger.info("Processing request") # Automatically includes request_id, method, path
return "Hello, World!"
if __name__ == "__main__":
app.run()
Automatic context injection:
request_id- Unique identifier for each requestrequest_method- HTTP method (GET, POST, etc.)request_path- Request URL path
FastAPI Middleware
Basic Context Middleware
For simple request context binding, use FastAPIRequestContextMiddleware:
from fastapi import FastAPI
from ganicas_utils.logging import LoggingConfigurator
from ganicas_utils.logging.middlewares import FastAPIRequestContextMiddleware
from ganicas_utils.config import Config
import structlog
config = Config()
LoggingConfigurator(
service_name=config.APP_NAME,
log_level="INFO",
setup_logging_dict=True,
).configure_structlog(formatter='json_formatter', formatter_std_lib='json_formatter')
logger = structlog.get_logger(__name__)
app = FastAPI()
app.add_middleware(FastAPIRequestContextMiddleware)
@app.get("/")
async def root():
logger.info("Processing request") # Automatically includes request context
return {"message": "Hello World"}
Request Logging Middleware
For production-grade request/response logging with advanced features, use RequestLoggingMiddleware:
from fastapi import FastAPI
from ganicas_utils.logging import LoggingConfigurator
from ganicas_utils.logging.middlewares import RequestLoggingMiddleware
import structlog
LoggingConfigurator(
service_name="my-api",
log_level="INFO",
setup_logging_dict=True,
).configure_structlog(formatter='json_formatter', formatter_std_lib='json_formatter')
app = FastAPI()
# Add comprehensive request logging
app.add_middleware(
RequestLoggingMiddleware,
slow_request_threshold_ms=1000, # Warn on requests > 1s
propagate_request_id=True, # Add request_id to response headers
skip_paths={"/healthz", "/metrics"}, # Don't log health checks
sample_2xx_rate=0.1, # Sample 10% of successful requests
)
@app.get("/api/users/{user_id}")
async def get_user(user_id: int):
return {"user_id": user_id, "name": "John Doe"}
Features
Automatic Logging:
request.start- Logs when request beginsrequest.end- Logs when request completes (with status, duration, size)request.exception- Logs unhandled exceptions with full traceback
Logged Information:
- Request: method, path, query params, client IP, user agent, content type/length
- Response: status code, size, content type, duration
- Headers: Sanitized request/response headers (for 4xx/5xx errors)
- Performance: Request duration, slow request detection
Security:
- Automatic sanitization of sensitive headers (
Authorization,Cookie,X-API-Key) - Authorization header preserves scheme:
Bearer ***instead of exposing tokens - No request/response body logging (only sizes)
Performance Optimization:
- Skip logging for health checks and metrics endpoints
- Sample successful requests to reduce log volume
- Skip OPTIONS requests
- Configurable path prefixes to skip
Distributed Tracing:
- Supports W3C
traceparentheader - Falls back to
x-request-idorx-amzn-trace-id - Propagates request_id to response headers
Configuration Options
| Parameter | Type | Default | Description |
|---|---|---|---|
logger |
structlog.BoundLoggerBase |
structlog.get_logger("http") |
Custom logger instance |
slow_request_threshold_ms |
int |
None |
Threshold in ms to flag slow requests |
propagate_request_id |
bool |
True |
Add x-request-id to response headers |
skip_paths |
set[str] |
{"/healthz", "/metrics"} |
Exact paths to skip logging |
skip_prefixes |
tuple[str, ...] |
("/metrics",) |
Path prefixes to skip logging |
sample_2xx_rate |
float |
None |
Sample rate for 2xx/3xx responses (0.0-1.0) |
Example Logs
Successful Request:
{
"event": "request.end",
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"method": "GET",
"path": "/api/users/123",
"status_code": 200,
"duration_ms": 45,
"response_size": 256,
"client_ip": "192.168.1.100",
"user_agent": "Mozilla/5.0...",
"level": "info"
}
Slow Request Warning:
{
"event": "request.end",
"request_id": "550e8400-e29b-41d4-a716-446655440001",
"method": "POST",
"path": "/api/process",
"status_code": 200,
"duration_ms": 1523,
"slow_request": true,
"slow_threshold_ms": 1000,
"level": "warning"
}
Error with Sanitized Headers:
{
"event": "request.end",
"request_id": "550e8400-e29b-41d4-a716-446655440002",
"method": "POST",
"path": "/api/login",
"status_code": 401,
"duration_ms": 12,
"request_headers": {
"authorization": "Bearer ***",
"content-type": "application/json"
},
"level": "warning"
}
🎯 Why Structured Logging?
Traditional logging challenges:
- Plain text logs are hard to parse programmatically
- Difficult to filter and search in log aggregation tools
- Missing context makes debugging distributed systems challenging
Structured logging benefits:
- Machine-readable: JSON format for easy parsing by ELK, Datadog, Grafana Loki
- Rich context: Automatic correlation with request_id, user_id, transaction_id
- Better filtering: Query logs by any field (status_code, duration, user_id, etc.)
- Observability: Enhanced monitoring and alerting capabilities
- Debugging: Trace requests across microservices with distributed tracing support
This package uses structlog - a powerful library that enhances Python's standard logging with better context management and flexible log formatting.
🛠️ Development
Prerequisites
Install Poetry for dependency management:
curl -sSL https://install.python-poetry.org | python3 -
Setup
# Install dependencies
poetry install --with dev
# Run tests with coverage
poetry run pytest -v --cov=ganicas_utils
# Run tests with detailed output
poetry run pytest -rs --cov=ganicas_utils -s
# Run pre-commit hooks
poetry run pre-commit run --all-files
Running Tests
# Run all tests
poetry run pytest
# Run specific test file
poetry run pytest tests/test_request_logging_middleware.py
# Run with coverage report
poetry run pytest --cov=ganicas_utils --cov-report=html
Code Quality
This project uses:
- pytest for testing (99% coverage)
- ruff for linting and formatting
- pre-commit for automated checks
📄 License
Proprietary - Internal use only for Ganicas projects.
🤝 Contributing
This is an internal package. For questions or contributions, please contact the Ganicas development team.
📚 Additional Resources
Made with ❤️ by Ganicas Team
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ganicas_package-0.3.0.tar.gz.
File metadata
- Download URL: ganicas_package-0.3.0.tar.gz
- Upload date:
- Size: 13.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/1.8.2 CPython/3.12.3 Linux/6.11.0-21-generic
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
33a0a4ea0562d32f3d42d57a06c2e9d7576427df8d384509660d02a5bbda4855
|
|
| MD5 |
c809e3bc127ac3c8c92b64450cb0a722
|
|
| BLAKE2b-256 |
74b8a781e4d5f291a5a5c3cf12d4b3784268981233af22bb74e6e721139c8551
|
File details
Details for the file ganicas_package-0.3.0-py3-none-any.whl.
File metadata
- Download URL: ganicas_package-0.3.0-py3-none-any.whl
- Upload date:
- Size: 11.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/1.8.2 CPython/3.12.3 Linux/6.11.0-21-generic
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
570caf10613232b7732f62ae6c60cc18342789594934b9b7dfcc46bfdc7f8042
|
|
| MD5 |
46778d1122358bea445b72fe252a074c
|
|
| BLAKE2b-256 |
2797a32dafaff82221520dfcfb3f47098d43bed8fe33f31f1cb277fc326a91c3
|