Skip to main content

Universal structured logging with exact JSON schema for Python frameworks

Project description

json-logify

Universal structured logging with exact JSON schema for Python frameworks.

PyPI version Python Support License: MIT

Features

  • Exact JSON Schema: Consistent log format across all frameworks
  • High Performance: Built with structlog and orjson for maximum speed
  • Universal: Works with Django, FastAPI, Flask and standalone Python
  • Security First: Automatic masking of sensitive data (passwords, tokens, etc.)
  • Distributed Tracing: Built-in trace_id support for Grafana/ELK filtering
  • Easy Setup: One-line configuration for most use cases
  • Rich Context: Request IDs, user tracking, and custom payload support
  • Smart Filtering: Configurable path ignoring and request/response body logging
  • Modern Python: Full type hints and async support

Quick Start

Installation

# Basic installation
pip install json-logify

# For specific frameworks
pip install json-logify[django]
# pip install json-logify[fastapi]  # Coming soon
# pip install json-logify[flask]    # Coming soon

# Everything
pip install json-logify[all]

Basic Usage

from logify import info, error, debug, warning, get_logger

# Basic logging with message
info("User logged in")

# With structured context
info("Payment processed", amount=100.0, currency="USD", user_id="user123")

# Named logger (shows module path in logs)
logger = get_logger(__name__)
logger.info("Processing started", task="data_import")

# With trace_id for distributed tracing (shows at top-level in JSON)
info("Request received", trace_id="abc-123-def")

# Different log levels
debug("Debug information", query_time=0.023)
warning("Slow database query detected", query_time=1.52, query_id="a1b2c3")
error("Payment failed", error_code="CARD_DECLINED", user_id="user123")

# Exception handling
try:
    result = some_function()
except Exception as e:
    error("Operation failed", error=e, operation="some_function")

Django Integration

1. Install with Django extras:

pip install json-logify[django]

2. Configure in settings.py:

from logify.django import get_logging_config

# Add middleware to MIDDLEWARE list
MIDDLEWARE = [
    # ... other middleware
    'logify.django.LogifyMiddleware',  # ← Add this
]

# Configure logging with json-logify
LOGGING = get_logging_config(
    service_name="my-django-app",
    level="INFO",
    max_string_length=200,              # String truncation limit
    sensitive_fields=[                  # Fields to mask with "***"
        "password", "passwd", "secret", "token", "api_key",
        "access_token", "refresh_token", "session_key",
        "credit_card", "cvv", "ssn", "authorization",
        "cookie", "x-api-key", "custom_sensitive_field"
    ],
    ignore_paths=[                      # Paths to skip logging
        "/health/", "/static/", "/favicon.ico",
        "/admin/jsi18n/", "/metrics/"
    ]
)

# Optional: Reduce Django built-in logger noise
LOGGING['loggers'].update({
    'django.utils.autoreload': {'level': 'WARNING'},
    'django.db.backends': {'level': 'WARNING'},
    'django.server': {'level': 'WARNING'},
    'django.request': {'level': 'WARNING'},
})

3. Use in your views:

from logify import info, error, get_logger

logger = get_logger(__name__)

def process_payment(request):
    # Log with automatic request context
    logger.info("Payment processing started",
         user_id=request.user.id,
         amount=request.POST.get('amount'))

    try:
        # Sensitive data gets automatically masked
        logger.info("User data received",
             username=request.user.username,    # ← Visible
             password=request.POST.get('password'),  # ← Masked: "***"
             email=request.user.email)               # ← Visible

        payment = process_payment_logic(request.POST)

        logger.info("Payment completed",
             payment_id=payment.id,
             status="success")

        return JsonResponse({"status": "success"})

    except Exception as e:
        error("Payment processing failed", error=e)
        return JsonResponse({"status": "error"}, status=500)

4. What you get automatically:

Request logging with trace_id:

{
  "timestamp": "2025-09-09T08:09:35.933Z",
  "level": "INFO",
  "message": "Request started",
  "logger": "logify.django",
  "trace_id": "b62e59b6-bae7-4a96-821d-abc123",
  "payload": {
    "request_id": "req-456-def",
    "service": "my-django-app",
    "method": "POST",
    "path": "/api/payment/",
    "user_info": "User ID: 123: john_doe",
    "headers": {
      "Content-Type": "application/json",
      "Authorization": "[FILTERED]",
      "X-Trace-ID": "b62e59b6-bae7-4a96-821d-abc123"
    },
    "request_body": {
      "username": "john_doe",
      "password": "***",
      "credit_card": "***"
    }
  }
}

Your application logs:

{
  "timestamp": "2025-09-09T08:09:35.934Z",
  "level": "INFO",
  "message": "Payment completed",
  "logger": "myapp.views.payments",
  "trace_id": "b62e59b6-bae7-4a96-821d-abc123",
  "payload": {
    "request_id": "req-456-def",
    "payment_id": "pay_123456",
    "status": "success"
  }
}

Log Schema

All logs follow this exact JSON schema:

{
  "timestamp": "2025-01-15T10:30:00.123Z",
  "level": "INFO",
  "message": "Log message here",
  "logger": "myapp.module.name",
  "trace_id": "abc-123-def-456",
  "error": "Error description (optional)",
  "payload": {
    "request_id": "req-123",
    "custom_field": "custom_value"
  }
}
Field Type Description
timestamp string ISO 8601 UTC timestamp
level string DEBUG, INFO, WARNING, ERROR, CRITICAL
message string Human-readable log message
logger string Logger name (module path via get_logger(__name__))
trace_id string/null Distributed trace ID for filtering in Grafana/ELK
error string Error description (only for errors)
payload object Custom fields and context

Distributed Tracing

trace_id enables filtering logs by request flow in Grafana, Kibana, or any log aggregator.

Automatic extraction (Django):

  • X-Trace-ID header
  • X-Trace-Id header
  • W3C traceparent header (format: 00-{trace_id}-{span_id}-{flags})

If no trace header is provided, a UUID is auto-generated.

Manual passing:

from logify import info
from logify.core import set_request_context

# Option 1: Pass directly
info("Processing", trace_id="my-trace-id")

# Option 2: Set in context (all subsequent logs will include it)
set_request_context(request_id="req-123", trace_id="trace-456")
info("Will include trace_id automatically")

Security Features

  • Automatic masking: Passwords, tokens, API keys, credit cards → "***"
  • Header filtering: Authorization, Cookie, X-API-Key → "[FILTERED]"
  • Recursive masking: Works in nested objects and arrays
  • Raw body scrubbing: Detects key=value patterns like password=secret
  • Request/Response body: Limited size + content-type filtering
  • Path ignoring: Skip health checks, static files, etc.

Configuring sensitive fields:

from logify.core import configure_logging

# Merge with defaults (recommended)
configure_logging(
    service_name="myapp",
    sensitive_fields=["my_custom_secret"]  # Added to defaults
)

# Or replace defaults entirely
configure_logging(
    service_name="myapp",
    sensitive_fields=["only_this_field"],
    replace_sensitive_defaults=True
)

Advanced Usage

Named Loggers

from logify import get_logger

# Creates logger with name shown in "logger" field
logger = get_logger(__name__)  # e.g., "myapp.services.payment"
logger.info("Processing payment")

Context Management

from logify import bind, set_request_context, clear_request_context

# Bind context to a logger
logger = bind(service="auth", module="login")
logger.info("Processing login", user_id="123")

# Set request-level context (useful in middleware)
set_request_context(request_id="req-456", trace_id="trace-789")
info("User action")  # Includes request context and trace_id
clear_request_context()

Performance Tracking

from logify import track_performance

@track_performance
def expensive_operation():
    # Your code here
    return "result"

# Automatically logs function start, completion, and duration

Requirements

  • Python 3.11+
  • structlog >= 23.0.0
  • orjson >= 3.8.0

License

MIT License - see LICENSE file for details.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

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

json_logify-0.1.7.tar.gz (27.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

json_logify-0.1.7-py3-none-any.whl (17.7 kB view details)

Uploaded Python 3

File details

Details for the file json_logify-0.1.7.tar.gz.

File metadata

  • Download URL: json_logify-0.1.7.tar.gz
  • Upload date:
  • Size: 27.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.12

File hashes

Hashes for json_logify-0.1.7.tar.gz
Algorithm Hash digest
SHA256 d14be60a9a49aa37522d17faba8bdb4ee82406079cf793519dc091132e0e183d
MD5 207a0f9b1d381d8c3aa04808dbfe21bc
BLAKE2b-256 eb94a6fe202ec0b04cd58ab55bee757125cf5ec08b190bf87c52d51da9d4931d

See more details on using hashes here.

File details

Details for the file json_logify-0.1.7-py3-none-any.whl.

File metadata

  • Download URL: json_logify-0.1.7-py3-none-any.whl
  • Upload date:
  • Size: 17.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.12

File hashes

Hashes for json_logify-0.1.7-py3-none-any.whl
Algorithm Hash digest
SHA256 a696683fb524e402b9d7f253d96133d96fd4347b4c1cef4c67724529d69abee5
MD5 d37e64baac3794094ad206bb7aed8221
BLAKE2b-256 c54f7b567205e796137c16d772d2274aa61e7bd1fbaf69ad0978fd93491955ea

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page