ganicas-package


Nameganicas-package JSON
Version 0.2.0 PyPI version JSON
download
home_pageNone
SummaryGanicas internal Python package for structured logging and utilities.
upload_time2025-10-11 18:47:54
maintainerNone
docs_urlNone
authorGanicas
requires_python<4.0,>=3.11
licenseNone
keywords logging utilities internal-package structlog middleware fastapi flask
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Ganicas Utils

[![Python Version](https://img.shields.io/badge/python-3.11%2B-blue.svg)](https://www.python.org/downloads/)
[![License](https://img.shields.io/badge/license-Proprietary-red.svg)](LICENSE)
[![Code Coverage](https://img.shields.io/badge/coverage-99%25-brightgreen.svg)](https://github.com/ganicas/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](https://www.structlog.org/), it enables production-ready, context-aware logging with minimal configuration.

---

## 📋 Table of Contents

- [Features](#-features)
- [Installation](#-installation)
- [Quick Start](#-quick-start)
- [Structured Logging](#-structured-logging)
  - [Basic Configuration](#basic-configuration)
  - [Production Configuration](#production-configuration)
- [Middleware](#-middleware)
  - [Flask Middleware](#flask-middleware)
  - [FastAPI Middleware](#fastapi-middleware)
  - [Request Logging Middleware](#request-logging-middleware)
- [Why Structured Logging?](#-why-structured-logging)
- [Development](#-development)
- [License](#-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

```bash
pip install ganicas-package
```

Or with Poetry:

```bash
poetry add ganicas-package
```

---

## 🚀 Quick Start

### Basic Configuration

Replace `logger = logging.getLogger(__name__)` with `logger = structlog.get_logger(__name__)`:

```python
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")
```

![basic example](images/plain_console_logger.png)

---

## 📊 Structured Logging

### Production Configuration

For production environments, use JSON formatting for machine-readable logs:

```python
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")
```

![logger with different keys](images/json_logger.png)


---

## 🔧 Middleware

### Flask Middleware

The `FlaskRequestContextMiddleware` automatically adds request context to all logs:

```python
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()
```

![logger with context flask](images/flask_logger_with_context.png)

**Automatic context injection:**
- `request_id` - Unique identifier for each request
- `request_method` - HTTP method (GET, POST, etc.)
- `request_path` - Request URL path

---

### FastAPI Middleware

#### Basic Context Middleware

For simple request context binding, use `FastAPIRequestContextMiddleware`:

```python
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"}
```

![logger with context fastapi](images/fastapi_logger_with_context.png)

---

### Request Logging Middleware

For production-grade request/response logging with advanced features, use `RequestLoggingMiddleware`:

```python
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 begins
- `request.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 `traceparent` header
- Falls back to `x-request-id` or `x-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:**
```json
{
  "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:**
```json
{
  "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:**
```json
{
  "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](https://www.structlog.org/)** - a powerful library that enhances Python's standard logging with better context management and flexible log formatting.


---

## 🛠️ Development

### Prerequisites

Install [Poetry](https://python-poetry.org/docs/#installation) for dependency management:

```bash
curl -sSL https://install.python-poetry.org | python3 -
```

### Setup

```bash
# 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

```bash
# 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

- [structlog Documentation](https://www.structlog.org/en/stable/)
- [FastAPI Middleware Guide](https://fastapi.tiangolo.com/tutorial/middleware/)
- [Flask Middleware Guide](https://flask.palletsprojects.com/en/latest/api/#flask.Flask.wsgi_app)

---

**Made with ❤️ by Ganicas Team**

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "ganicas-package",
    "maintainer": null,
    "docs_url": null,
    "requires_python": "<4.0,>=3.11",
    "maintainer_email": null,
    "keywords": "logging, utilities, internal-package, structlog, middleware, fastapi, flask",
    "author": "Ganicas",
    "author_email": null,
    "download_url": "https://files.pythonhosted.org/packages/ce/4f/bf7aaf04d8385a1399cebd1b2d5b7769d9508312dc7ae1155b76974a9045/ganicas_package-0.2.0.tar.gz",
    "platform": null,
    "description": "# Ganicas Utils\n\n[![Python Version](https://img.shields.io/badge/python-3.11%2B-blue.svg)](https://www.python.org/downloads/)\n[![License](https://img.shields.io/badge/license-Proprietary-red.svg)](LICENSE)\n[![Code Coverage](https://img.shields.io/badge/coverage-99%25-brightgreen.svg)](https://github.com/ganicas/ganicas_utils)\n\n**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.\n\n---\n\n## \ud83d\udccb Table of Contents\n\n- [Features](#-features)\n- [Installation](#-installation)\n- [Quick Start](#-quick-start)\n- [Structured Logging](#-structured-logging)\n  - [Basic Configuration](#basic-configuration)\n  - [Production Configuration](#production-configuration)\n- [Middleware](#-middleware)\n  - [Flask Middleware](#flask-middleware)\n  - [FastAPI Middleware](#fastapi-middleware)\n  - [Request Logging Middleware](#request-logging-middleware)\n- [Why Structured Logging?](#-why-structured-logging)\n- [Development](#-development)\n- [License](#-license)\n\n---\n\n## \u2728 Features\n\n- **Structured Logging**: JSON-formatted logs for easy parsing by log aggregation tools (ELK, Datadog, Grafana Loki)\n- **Context Management**: Automatic request context binding (request_id, IP, user_agent, etc.)\n- **Flask & FastAPI Support**: Ready-to-use middleware for both frameworks\n- **Advanced Request Logging**: Comprehensive ASGI middleware with:\n  - Automatic request/response logging\n  - Sensitive header sanitization\n  - Slow request detection\n  - Sampling for high-traffic endpoints\n  - Exception tracking with full context\n  - Distributed tracing support (traceparent header)\n- **Production Ready**: Battle-tested with 99% code coverage\n\n---\n\n## \ud83d\udce6 Installation\n\n```bash\npip install ganicas-package\n```\n\nOr with Poetry:\n\n```bash\npoetry add ganicas-package\n```\n\n---\n\n## \ud83d\ude80 Quick Start\n\n### Basic Configuration\n\nReplace `logger = logging.getLogger(__name__)` with `logger = structlog.get_logger(__name__)`:\n\n```python\nfrom ganicas_utils.logging import LoggingConfigurator\nfrom ganicas_utils.config import Config\nimport structlog\n\nconfig = Config()\n\nLoggingConfigurator(\n    service_name=config.APP_NAME,\n    log_level='INFO',\n    setup_logging_dict=True\n).configure_structlog(\n    formatter='plain_console',\n    formatter_std_lib='plain_console'\n)\n\nlogger = structlog.get_logger(__name__)\nlogger.info(\"Application started\", version=\"1.0.0\", environment=\"production\")\n```\n\n![basic example](images/plain_console_logger.png)\n\n---\n\n## \ud83d\udcca Structured Logging\n\n### Production Configuration\n\nFor production environments, use JSON formatting for machine-readable logs:\n\n```python\nfrom ganicas_utils.logging import LoggingConfigurator\nfrom ganicas_utils.config import Config\nimport structlog\n\nconfig = Config()\n\nLoggingConfigurator(\n    service_name=config.APP_NAME,\n    log_level='INFO',\n    setup_logging_dict=True\n).configure_structlog(\n    formatter='json_formatter',\n    formatter_std_lib='json_formatter'\n)\n\nlogger = structlog.get_logger(__name__)\nlogger.info(\"User login\", user_id=12345, ip_address=\"192.168.1.1\")\nlogger.warning(\"High memory usage\", memory_percent=85.5, threshold=80)\nlogger.error(\"Database connection failed\", db_host=\"localhost\", error_code=\"CONN_REFUSED\")\n\ntry:\n    result = 1 / 0\nexcept ZeroDivisionError:\n    logger.exception(\"Division by zero error\", operation=\"calculate_ratio\")\n```\n\n![logger with different keys](images/json_logger.png)\n\n\n---\n\n## \ud83d\udd27 Middleware\n\n### Flask Middleware\n\nThe `FlaskRequestContextMiddleware` automatically adds request context to all logs:\n\n```python\nimport uuid\nfrom flask import Flask\nfrom ganicas_utils.logging import LoggingConfigurator\nfrom ganicas_utils.logging.middlewares import FlaskRequestContextMiddleware\nfrom ganicas_utils.config import Config\nimport structlog\n\nconfig = Config()\n\nLoggingConfigurator(\n    service_name=config.APP_NAME,\n    log_level=\"INFO\",\n    setup_logging_dict=True,\n).configure_structlog(formatter='json_formatter', formatter_std_lib='json_formatter')\n\nlogger = structlog.get_logger(__name__)\n\napp = Flask(__name__)\napp.wsgi_app = FlaskRequestContextMiddleware(app.wsgi_app)\n\n@app.route(\"/\")\ndef home():\n    logger.info(\"Processing request\")  # Automatically includes request_id, method, path\n    return \"Hello, World!\"\n\nif __name__ == \"__main__\":\n    app.run()\n```\n\n![logger with context flask](images/flask_logger_with_context.png)\n\n**Automatic context injection:**\n- `request_id` - Unique identifier for each request\n- `request_method` - HTTP method (GET, POST, etc.)\n- `request_path` - Request URL path\n\n---\n\n### FastAPI Middleware\n\n#### Basic Context Middleware\n\nFor simple request context binding, use `FastAPIRequestContextMiddleware`:\n\n```python\nfrom fastapi import FastAPI\nfrom ganicas_utils.logging import LoggingConfigurator\nfrom ganicas_utils.logging.middlewares import FastAPIRequestContextMiddleware\nfrom ganicas_utils.config import Config\nimport structlog\n\nconfig = Config()\n\nLoggingConfigurator(\n    service_name=config.APP_NAME,\n    log_level=\"INFO\",\n    setup_logging_dict=True,\n).configure_structlog(formatter='json_formatter', formatter_std_lib='json_formatter')\n\nlogger = structlog.get_logger(__name__)\napp = FastAPI()\napp.add_middleware(FastAPIRequestContextMiddleware)\n\n@app.get(\"/\")\nasync def root():\n    logger.info(\"Processing request\")  # Automatically includes request context\n    return {\"message\": \"Hello World\"}\n```\n\n![logger with context fastapi](images/fastapi_logger_with_context.png)\n\n---\n\n### Request Logging Middleware\n\nFor production-grade request/response logging with advanced features, use `RequestLoggingMiddleware`:\n\n```python\nfrom fastapi import FastAPI\nfrom ganicas_utils.logging import LoggingConfigurator\nfrom ganicas_utils.logging.middlewares import RequestLoggingMiddleware\nimport structlog\n\nLoggingConfigurator(\n    service_name=\"my-api\",\n    log_level=\"INFO\",\n    setup_logging_dict=True,\n).configure_structlog(formatter='json_formatter', formatter_std_lib='json_formatter')\n\napp = FastAPI()\n\n# Add comprehensive request logging\napp.add_middleware(\n    RequestLoggingMiddleware,\n    slow_request_threshold_ms=1000,      # Warn on requests > 1s\n    propagate_request_id=True,           # Add request_id to response headers\n    skip_paths={\"/healthz\", \"/metrics\"}, # Don't log health checks\n    sample_2xx_rate=0.1,                 # Sample 10% of successful requests\n)\n\n@app.get(\"/api/users/{user_id}\")\nasync def get_user(user_id: int):\n    return {\"user_id\": user_id, \"name\": \"John Doe\"}\n```\n\n#### Features\n\n**Automatic Logging:**\n- `request.start` - Logs when request begins\n- `request.end` - Logs when request completes (with status, duration, size)\n- `request.exception` - Logs unhandled exceptions with full traceback\n\n**Logged Information:**\n- Request: method, path, query params, client IP, user agent, content type/length\n- Response: status code, size, content type, duration\n- Headers: Sanitized request/response headers (for 4xx/5xx errors)\n- Performance: Request duration, slow request detection\n\n**Security:**\n- Automatic sanitization of sensitive headers (`Authorization`, `Cookie`, `X-API-Key`)\n- Authorization header preserves scheme: `Bearer ***` instead of exposing tokens\n- No request/response body logging (only sizes)\n\n**Performance Optimization:**\n- Skip logging for health checks and metrics endpoints\n- Sample successful requests to reduce log volume\n- Skip OPTIONS requests\n- Configurable path prefixes to skip\n\n**Distributed Tracing:**\n- Supports W3C `traceparent` header\n- Falls back to `x-request-id` or `x-amzn-trace-id`\n- Propagates request_id to response headers\n\n#### Configuration Options\n\n| Parameter | Type | Default | Description |\n|-----------|------|---------|-------------|\n| `logger` | `structlog.BoundLoggerBase` | `structlog.get_logger(\"http\")` | Custom logger instance |\n| `slow_request_threshold_ms` | `int` | `None` | Threshold in ms to flag slow requests |\n| `propagate_request_id` | `bool` | `True` | Add `x-request-id` to response headers |\n| `skip_paths` | `set[str]` | `{\"/healthz\", \"/metrics\"}` | Exact paths to skip logging |\n| `skip_prefixes` | `tuple[str, ...]` | `(\"/metrics\",)` | Path prefixes to skip logging |\n| `sample_2xx_rate` | `float` | `None` | Sample rate for 2xx/3xx responses (0.0-1.0) |\n\n#### Example Logs\n\n**Successful Request:**\n```json\n{\n  \"event\": \"request.end\",\n  \"request_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n  \"method\": \"GET\",\n  \"path\": \"/api/users/123\",\n  \"status_code\": 200,\n  \"duration_ms\": 45,\n  \"response_size\": 256,\n  \"client_ip\": \"192.168.1.100\",\n  \"user_agent\": \"Mozilla/5.0...\",\n  \"level\": \"info\"\n}\n```\n\n**Slow Request Warning:**\n```json\n{\n  \"event\": \"request.end\",\n  \"request_id\": \"550e8400-e29b-41d4-a716-446655440001\",\n  \"method\": \"POST\",\n  \"path\": \"/api/process\",\n  \"status_code\": 200,\n  \"duration_ms\": 1523,\n  \"slow_request\": true,\n  \"slow_threshold_ms\": 1000,\n  \"level\": \"warning\"\n}\n```\n\n**Error with Sanitized Headers:**\n```json\n{\n  \"event\": \"request.end\",\n  \"request_id\": \"550e8400-e29b-41d4-a716-446655440002\",\n  \"method\": \"POST\",\n  \"path\": \"/api/login\",\n  \"status_code\": 401,\n  \"duration_ms\": 12,\n  \"request_headers\": {\n    \"authorization\": \"Bearer ***\",\n    \"content-type\": \"application/json\"\n  },\n  \"level\": \"warning\"\n}\n```\n\n---\n\n## \ud83c\udfaf Why Structured Logging?\n\n**Traditional logging challenges:**\n- Plain text logs are hard to parse programmatically\n- Difficult to filter and search in log aggregation tools\n- Missing context makes debugging distributed systems challenging\n\n**Structured logging benefits:**\n- **Machine-readable**: JSON format for easy parsing by ELK, Datadog, Grafana Loki\n- **Rich context**: Automatic correlation with request_id, user_id, transaction_id\n- **Better filtering**: Query logs by any field (status_code, duration, user_id, etc.)\n- **Observability**: Enhanced monitoring and alerting capabilities\n- **Debugging**: Trace requests across microservices with distributed tracing support\n\n**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.\n\n\n---\n\n## \ud83d\udee0\ufe0f Development\n\n### Prerequisites\n\nInstall [Poetry](https://python-poetry.org/docs/#installation) for dependency management:\n\n```bash\ncurl -sSL https://install.python-poetry.org | python3 -\n```\n\n### Setup\n\n```bash\n# Install dependencies\npoetry install --with dev\n\n# Run tests with coverage\npoetry run pytest -v --cov=ganicas_utils\n\n# Run tests with detailed output\npoetry run pytest -rs --cov=ganicas_utils -s\n\n# Run pre-commit hooks\npoetry run pre-commit run --all-files\n```\n\n### Running Tests\n\n```bash\n# Run all tests\npoetry run pytest\n\n# Run specific test file\npoetry run pytest tests/test_request_logging_middleware.py\n\n# Run with coverage report\npoetry run pytest --cov=ganicas_utils --cov-report=html\n```\n\n### Code Quality\n\nThis project uses:\n- **pytest** for testing (99% coverage)\n- **ruff** for linting and formatting\n- **pre-commit** for automated checks\n\n---\n\n## \ud83d\udcc4 License\n\nProprietary - Internal use only for Ganicas projects.\n\n---\n\n## \ud83e\udd1d Contributing\n\nThis is an internal package. For questions or contributions, please contact the Ganicas development team.\n\n---\n\n## \ud83d\udcda Additional Resources\n\n- [structlog Documentation](https://www.structlog.org/en/stable/)\n- [FastAPI Middleware Guide](https://fastapi.tiangolo.com/tutorial/middleware/)\n- [Flask Middleware Guide](https://flask.palletsprojects.com/en/latest/api/#flask.Flask.wsgi_app)\n\n---\n\n**Made with \u2764\ufe0f by Ganicas Team**\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "Ganicas internal Python package for structured logging and utilities.",
    "version": "0.2.0",
    "project_urls": null,
    "split_keywords": [
        "logging",
        " utilities",
        " internal-package",
        " structlog",
        " middleware",
        " fastapi",
        " flask"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "48a660b54bfd632666dc4b85cc59b53c2d517355f2cc6ff755b153bb0497713a",
                "md5": "c7227631b22a86db4970ab1a51cbb977",
                "sha256": "6a879efd18a1a02aa0527ba1e203a438f713b77a34cda5b87911ac38942f4096"
            },
            "downloads": -1,
            "filename": "ganicas_package-0.2.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "c7227631b22a86db4970ab1a51cbb977",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": "<4.0,>=3.11",
            "size": 11540,
            "upload_time": "2025-10-11T18:47:53",
            "upload_time_iso_8601": "2025-10-11T18:47:53.122058Z",
            "url": "https://files.pythonhosted.org/packages/48/a6/60b54bfd632666dc4b85cc59b53c2d517355f2cc6ff755b153bb0497713a/ganicas_package-0.2.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "ce4fbf7aaf04d8385a1399cebd1b2d5b7769d9508312dc7ae1155b76974a9045",
                "md5": "d8675d7bd7956db97bc73e42b1b490a1",
                "sha256": "f0b17347f4825df5450a9bf11118b551be00b14b8a8bfd284c2175f97c7153b2"
            },
            "downloads": -1,
            "filename": "ganicas_package-0.2.0.tar.gz",
            "has_sig": false,
            "md5_digest": "d8675d7bd7956db97bc73e42b1b490a1",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": "<4.0,>=3.11",
            "size": 13720,
            "upload_time": "2025-10-11T18:47:54",
            "upload_time_iso_8601": "2025-10-11T18:47:54.144532Z",
            "url": "https://files.pythonhosted.org/packages/ce/4f/bf7aaf04d8385a1399cebd1b2d5b7769d9508312dc7ae1155b76974a9045/ganicas_package-0.2.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-10-11 18:47:54",
    "github": false,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "lcname": "ganicas-package"
}
        
Elapsed time: 1.65930s