Skip to main content

A flexible structured JSON logger for Python applications

Project description

Structured Logger

A flexible, configurable structured JSON logger for Python applications. Perfect for cloud deployments, containerized applications, and log aggregation systems like ELK, Splunk, or cloud logging services.

Features

  • 🚀 Structured JSON logging with automatic serialization of complex objects
  • ⚙️ Highly configurable with sensible defaults
  • 🌍 Environment-aware formatting (JSON for production, readable for development)
  • 🔧 Custom field support for tracing, user context, and more
  • 📦 Easy integration with existing Python logging
  • 🎯 Type-safe with full type hints
  • Zero dependencies - uses only Python standard library

Installation

pip install structured-logger

Quick Start

Basic Usage

from structured_logger import get_logger

logger = get_logger(__name__)

logger.info("Application started")
logger.error("Something went wrong", extra={"user_id": "12345"})

Output in development:

2024-01-15 10:30:45,123 [INFO] myapp: Application started
2024-01-15 10:30:45,124 [ERROR] myapp: Something went wrong

Output in production:

{"time": "2024-01-15 10:30:45,123", "level": "INFO", "message": "Application started", "module": "myapp"}
{"time": "2024-01-15 10:30:45,124", "level": "ERROR", "message": "Something went wrong", "module": "myapp", "user_id": "12345"}

Custom Configuration

from structured_logger import get_logger, LoggerConfig

# Custom configuration
config = LoggerConfig(
    custom_fields=["user_id", "request_id", "trace_id"],
    production_env_vars=["ENV", "ENVIRONMENT"],
    production_env_values=["prod", "production", "staging"],
    dev_format="%(asctime)s [%(levelname)s] %(name)s: %(message)s"
)

logger = get_logger(__name__, config=config)

Force JSON or Development Format

# Always use JSON formatting
logger = get_logger(__name__, force_json=True)

# Always use development formatting
logger = get_logger(__name__, force_dev=True)

Root Logger Setup

from structured_logger import setup_root_logger

# Setup root logger for entire application
setup_root_logger()

# Now all loggers will use structured format
import logging
logger = logging.getLogger("myapp")
logger.info("This will be structured")

Advanced Usage

Custom Serializers

from datetime import datetime
from structured_logger import LoggerConfig, get_logger

def serialize_datetime(dt):
    return dt.isoformat()

config = LoggerConfig(
    custom_serializers={
        datetime: serialize_datetime
    }
)

logger = get_logger(__name__, config=config)
logger.info("Current time", extra={"timestamp": datetime.now()})

Context Fields

import logging
from structured_logger import get_logger

logger = get_logger(__name__)

# Add context to log record
class ContextFilter(logging.Filter):
    def filter(self, record):
        record.user_id = getattr(self, 'user_id', None)
        record.request_id = getattr(self, 'request_id', None)
        return True

context_filter = ContextFilter()
logger.addFilter(context_filter)

# Set context
context_filter.user_id = "user123"
context_filter.request_id = "req456"

logger.info("Processing request")  # Will include user_id and request_id

Exception Logging

try:
    raise ValueError("Something went wrong")
except Exception:
    logger.exception("An error occurred", extra={"operation": "data_processing"})

JSON Output:

{
  "time": "2024-01-15 10:30:45,123",
  "level": "ERROR", 
  "message": "An error occurred",
  "module": "myapp",
  "operation": "data_processing",
  "exception": "Traceback (most recent call last):\n  File \"example.py\", line 2, in <module>\n    raise ValueError(\"Something went wrong\")\nValueError: Something went wrong"
}

Configuration Options

LoggerConfig Parameters

Parameter Type Default Description
production_env_vars List[str] ["RAILWAY_ENVIRONMENT", "ENV", "ENVIRONMENT", "NODE_ENV"] Environment variables to check for production
production_env_values List[str] ["prod", "production", "staging"] Values that indicate production environment
log_level_env_var str "LOG_LEVEL" Environment variable for log level
default_log_level str "INFO" Default log level
custom_fields List[str] ["user_id", "company_id", "request_id", "trace_id", "span_id"] Fields to extract from log records
time_format Optional[str] None Custom time format
dev_format str "%(asctime)s [%(levelname)s] %(name)s: %(message)s" Format string for development
custom_serializers Dict[type, Callable] {} Custom serializers for specific types
include_extra_attrs bool True Whether to include extra attributes
excluded_attrs List[str] Standard logging fields Fields to exclude from extra attributes

Environment Variables

Variable Description Default
LOG_LEVEL Set logging level INFO
RAILWAY_ENVIRONMENT Railway deployment indicator -
ENV Environment indicator -
ENVIRONMENT Environment indicator -
NODE_ENV Node.js style environment -

Framework Integration

Flask

from flask import Flask, request, g
from structured_logger import get_logger
import uuid

app = Flask(__name__)
logger = get_logger(__name__)

@app.before_request
def before_request():
    g.request_id = str(uuid.uuid4())

@app.after_request
def after_request(response):
    logger.info(
        "Request processed",
        extra={
            "request_id": getattr(g, 'request_id', None),
            "method": request.method,
            "path": request.path,
            "status": response.status_code
        }
    )
    return response

FastAPI

from fastapi import FastAPI, Request
from structured_logger import get_logger
import uuid
import time

app = FastAPI()
logger = get_logger(__name__)

@app.middleware("http")
async def log_requests(request: Request, call_next):
    request_id = str(uuid.uuid4())
    start_time = time.time()
    
    response = await call_next(request)
    
    process_time = time.time() - start_time
    logger.info(
        "Request processed",
        extra={
            "request_id": request_id,
            "method": request.method,
            "path": request.url.path,
            "status": response.status_code,
            "duration": process_time
        }
    )
    
    return response

Django

# settings.py
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'structured': {
            'level': 'INFO',
            'class': 'structured_logger.logger.StructuredLogHandler',
        },
    },
    'loggers': {
        'django': {
            'handlers': ['structured'],
            'level': 'INFO',
            'propagate': True,
        },
        'myapp': {
            'handlers': ['structured'],
            'level': 'INFO',
            'propagate': True,
        },
    },
}

Railway.app Compatibility

This library maintains full compatibility with Railway.app deployments. The original Railway-specific functionality is preserved:

# These work exactly like before
from structured_logger import get_railway_logger

logger = get_railway_logger(__name__)
logger.info("Deployed to Railway!")

Development

# Clone the repository
git clone https://github.com/yourusername/structured-logger.git
cd structured-logger

# Install development dependencies
pip install -e .

# Run examples
python examples/basic_usage.py
python examples/custom_config.py

Contributing

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

License

This project is licensed under the MIT License - see the LICENSE file for details.

Changelog

1.0.0

  • Initial release
  • Flexible configuration system
  • Environment-aware formatting
  • Custom serializers support
  • Full backward compatibility with Railway logger

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

structured_logger_railway-1.0.0.tar.gz (13.1 kB view details)

Uploaded Source

Built Distribution

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

structured_logger_railway-1.0.0-py3-none-any.whl (8.7 kB view details)

Uploaded Python 3

File details

Details for the file structured_logger_railway-1.0.0.tar.gz.

File metadata

File hashes

Hashes for structured_logger_railway-1.0.0.tar.gz
Algorithm Hash digest
SHA256 470c7f30742add35ac8b64657911ad8d5fb74596ff476fe1d739960d3ce71c0b
MD5 9cbe4063b596e473842c75b48e64b0e7
BLAKE2b-256 2e4f27edbe50e5e650744b8265ae3de873f2f5ca06e6b4e183baf29b5500ccda

See more details on using hashes here.

File details

Details for the file structured_logger_railway-1.0.0-py3-none-any.whl.

File metadata

File hashes

Hashes for structured_logger_railway-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ca9943149256b01749800814517f9529cf2a2ea2dd1bb1229b25b2ed73e2e0d0
MD5 8f1b22f6e872c2405ec0dc892750d92f
BLAKE2b-256 eb98c1d008120586b50a6778de0599301b2afda5d7cc2daccf15298c572447f1

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