Skip to main content

Utilities for logging, assertions and custom exceptions

Project description

Logguard

Supported Python Versions PyPI codecov

Logguard is a lightweight logging and assertion library designed to make it easy to capture rich context and structured logs in Python applications. It provides a simple API for logging with automatic source capture, flexible configuration, and a semantic exception hierarchy.

It is built on top of the logging and rich libraries for enhanced logging capabilities.

Features

  • Easy Configuration: Set up logging in one line with AppLogger
  • File Rotation: Automatic log rotation with configurable size and backup count
  • Rich Console Output: Beautiful console logs with Rich support
  • JSON Logging: Optional structured JSON output for log aggregation systems
  • Environment-Aware Assertions: CHECK, ASSERT, ENSURE, VERIFY with context
  • Specialized Helpers: ASSERT_TYPE, ASSERT_IN_RANGE, ASSERT_NOT_NULL, etc.
  • Semantic Exceptions: Clear exception hierarchy (ValidationError, AssertFailure, etc.)
  • Fast Startup: Lazy imports for minimal performance impact
  • Library Silencing: Automatically suppresses noisy third-party library logs

Installation

Install with pip or your favorite PyPI package manager.

python -m pip install py-logguard

For development with optional dependencies:

python -m pip install py-logguard[dev]

For JSON logging support:

python -m pip install py-logguard[json]

Quick Start

Logging

Logguard provides a simple way to configure logging with sensible defaults. Just call setup() once and you're ready to go.

from logguard import AppLogger

AppLogger.setup(log_file="logs/app.log", console_level="INFO")
logger = AppLogger.get_logger(__name__)

logger.info("Application started")
logger.debug("Debug info")
logger.warning("Warning")
logger.error("Error occurred")

Logging will output to both console and file, with automatic rotation when files get too large.

Assertions

Logguard provides environment-aware assertions that adapt their behavior based on APP_ENV:

Assertion Development Production Use Case
CHECK Raises Raises Critical invariants
ASSERT Raises Ignored Debug checks
ENSURE Raises Logs Preconditions
VERIFY Raises Logs Postconditions
from logguard import CHECK, ASSERT, ENSURE, VERIFY

# CHECK: Always raises - use for critical invariants
CHECK(config is not None, "Configuration required", component="auth")

# ASSERT: Raises in dev, ignored in prod - use for debugging
ASSERT(age > 0, "Age must be positive", age=age)

# ENSURE: Raises in dev, logs in prod - use for preconditions
def process_order(order):
    ENSURE(order.is_valid(), "Invalid order", order_id=order.id)

# VERIFY: Raises in dev, logs in prod - use for postconditions
result = calculate_total(items)
VERIFY(result >= 0, "Total cannot be negative", result=result)

Configure environment via APP_ENV variable:

  • Development: dev, development, local, test
  • Production: prod, production
Specialized Assertions

Logguard includes helpers for common validation patterns:

from logguard import (
    ASSERT_NOT_NULL,
    ASSERT_TYPE,
    ASSERT_IN_RANGE,
    ASSERT_EQUALS,
    ASSERT_IN,
    ASSERT_NOT_EMPTY,
    ASSERT_GREATER,
    ASSERT_LESS,
)

# Null checks
ASSERT_NOT_NULL(user, "User is required")

# Type checking
ASSERT_TYPE(config, dict, "Expected dictionary")

# Range validation (inclusive)
ASSERT_IN_RANGE(percentage, 0, 100, "Invalid percentage")

# Equality
ASSERT_EQUALS(response.status, 200, "Expected success")

# Membership
ASSERT_IN(status, ["pending", "active"], "Invalid status")

# Non-empty
ASSERT_NOT_EMPTY(items, "Items list cannot be empty")

# Comparisons
ASSERT_GREATER(balance, 0, "Balance must be positive")
ASSERT_LESS(retry_count, max_retries, "Too many retries")

Each helper raises a specific exception type (NullError, TypeErrorAssert, RangeError, etc.) for precise error handling.

Custom Exceptions

Logguard provides a semantic exception hierarchy for clearer error handling:

from logguard import (
    LogGuardError,
    ValidationError,
    AssertFailure,
    NullError,
    RangeError,
    ConfigurationError,
    ResourceNotFoundError,
    ForbiddenError,
)

# All exceptions include rich context
try:
    raise ValidationError("Invalid email", context={"field": "email", "value": "bad@"})
except LogGuardError as e:
    print(f"Error: {e}")
    print(f"Details: {e.to_dict()}")
    # {'type': 'ValidationError', 'message': 'Invalid email', 'context': {'field': 'email', 'value': 'bad@'}}

# Catch specific assertion failures
try:
    ASSERT_IN_RANGE(age, 0, 150)
except RangeError as e:
    handle_invalid_age(e.context)

# Resource errors
raise ResourceNotFoundError("User", identifier=123)
raise ForbiddenError("Access denied", user_id=456)

Exception Hierarchy:

LogGuardError
├── ConfigurationError
│   └── MissingConfigError
├── ValidationError
│   └── AssertFailure
│       ├── NullError
│       ├── RangeError
│       ├── TypeErrorAssert
│       ├── EmptyError
│       ├── EqualsError
│       ├── ComparisonError
│       └── MembershipError
└── ResourceError
    ├── ResourceNotFoundError
    └── ForbiddenError
Configuration

Configure the assertion system programmatically:

from logguard import AssertionConfig, AssertionManager

# Switch to production mode (ASSERT becomes no-op)
AssertionManager.configure(
    AssertionConfig(environment="production", enable_asserts=True)
)

# Custom failure handlers
def my_raise_handler(message, context, exception_class):
    # Log to Sentry, then raise
    sentry.capture_message(message, extra=context)
    raise exception_class(message, context=context)

def my_log_handler(message, context, exception_class):
    # Custom logging
    metrics.increment("assertion_failures")
    logger.warning(f"{exception_class.__name__}: {message}")

AssertionManager.set_failure_strategy(
    raise_strategy=my_raise_handler,
    log_strategy=my_log_handler,
)

# Reset to defaults (useful for testing)
AssertionManager.reset()
JSON Logging

For production environments, enable JSON logs for structured log aggregation:

from logguard import AppLogger

AppLogger.setup(json_logs=True, log_file="logs/app.json")
logger = AppLogger.get_logger(__name__)

logger.info("User logged in", extra={"user_id": 123})
logger.error("Database error", extra={"error_code": "DB_001"})

Requires: pip install py-logguard[json]


Note: Check out examples/demo.py for a complete, interactive demonstration of all LogGuard features with Rich console output!

Run it with:

python examples/demo.py

API Reference

AppLogger - Logging Configuration

AppLogger is the main interface for logging. Configure it once and get loggers throughout your application.

Setup:

AppLogger.setup(
    log_file="logs/app.log",      # Path to log file
    console_level="INFO",          # Console output level
    file_level="DEBUG",            # File output level
    json_logs=False,               # Enable JSON formatting
    max_bytes=5_000_000,           # Max file size before rotation
    backup_count=3,                # Number of backups to keep
    delay=True,                    # Delay file creation until first log
)

HandlerType enum: use instead of plain strings to select which handlers an operation targets:

Member Targets
HandlerType.ALL Every handler (default)
HandlerType.CONSOLE Console / RichHandler only
HandlerType.FILE RotatingFileHandler only
HandlerType.JSON JSON RotatingFileHandler only

Methods:

  • get_logger(name: str | None = None): Get or create a logger instance
  • set_level(level, handler_type: HandlerType | str = HandlerType.ALL): Change log level on selected handlers at runtime
  • set_format(format_string, handler_type: HandlerType | str = HandlerType.ALL, datefmt=None): Change the format string on selected handlers; RichHandler and JsonFormatter instances are skipped automatically
  • silence_noisy_libraries(modules: list[str] | None = None): Suppress third-party logs
  • reset(): Reset configuration (useful for testing)
from logguard import AppLogger, HandlerType

AppLogger.setup(log_file="logs/app.log", console_level="INFO")

# Drop file handler to DEBUG without touching the console
AppLogger.set_level("DEBUG", HandlerType.FILE)

# Apply a compact format to file logs only
AppLogger.set_format("%(asctime)s | %(levelname)s | %(message)s", HandlerType.FILE)
Assertions - Environment-Aware Validation

Core Assertions:

Function Behavior
CHECK(condition, message, **context) Always raises AssertFailure
ASSERT(condition, message, **context) Raises in dev, ignored in prod
ENSURE(condition, message, **context) Raises in dev, logs in prod
VERIFY(condition, message, **context) Raises in dev, logs in prod

Specialized Helpers:

Function Exception Type
ASSERT_NOT_NULL(value, message) NullError
ASSERT_NULL(value, message) NullError
ASSERT_EQUALS(actual, expected, message) EqualsError
ASSERT_GREATER(a, b, message) ComparisonError
ASSERT_LESS(a, b, message) ComparisonError
ASSERT_IN_RANGE(value, min, max, message) RangeError
ASSERT_BETWEEN_EXCLUSIVE(value, min, max, message) RangeError
ASSERT_TYPE(value, expected, message) TypeErrorAssert
ASSERT_NOT_EMPTY(value, message) EmptyError
ASSERT_IN(item, container, message) MembershipError

Configuration Classes:

  • AssertionConfig(environment, enable_asserts) - Configuration dataclass
  • AssertionManager - Central assertion engine with configure(), set_failure_strategy(), reset()
Exceptions - Semantic Error Hierarchy

All exceptions inherit from LogGuardError and include rich context information.

Properties:

  • message - Error description
  • context - Additional context data (dict)
  • .to_dict() - Serialize to dictionary for logging

Exception Types:

Exception Description
LogGuardError Base for all exceptions
ConfigurationError Invalid configuration
MissingConfigError Required config key missing
ValidationError Validation failures
AssertFailure Base for assertion failures
NullError Unexpected None value
RangeError Value out of range
TypeErrorAssert Wrong type
EmptyError Unexpected empty value
EqualsError Values not equal
ComparisonError Comparison failed
MembershipError Item not in container
ResourceError Resource-related errors
ResourceNotFoundError Resource not found
ForbiddenError Permission denied

Contributing

Contributions, issues and feature requests are welcome. Feel free to check the issues page.

License

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

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

py_logguard-0.3.2.tar.gz (38.3 kB view details)

Uploaded Source

Built Distribution

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

py_logguard-0.3.2-py3-none-any.whl (21.8 kB view details)

Uploaded Python 3

File details

Details for the file py_logguard-0.3.2.tar.gz.

File metadata

  • Download URL: py_logguard-0.3.2.tar.gz
  • Upload date:
  • Size: 38.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for py_logguard-0.3.2.tar.gz
Algorithm Hash digest
SHA256 a9293ff28da772831c8d33dcc4cafd4e1e80b9207b2fbde561f19041eaab7ad7
MD5 58ccc847f8e5deb26685a3bcba5d3de7
BLAKE2b-256 a6ab6a4d4e6f2d3e8cb34bcd4ddb6e9524484a13655abfab3fb87fc6e8d70c43

See more details on using hashes here.

Provenance

The following attestation bundles were made for py_logguard-0.3.2.tar.gz:

Publisher: release.yml on DMsuDev/logguard

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file py_logguard-0.3.2-py3-none-any.whl.

File metadata

  • Download URL: py_logguard-0.3.2-py3-none-any.whl
  • Upload date:
  • Size: 21.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for py_logguard-0.3.2-py3-none-any.whl
Algorithm Hash digest
SHA256 4a8c4fa9fbe475145d47c040754603e11caca230763d9157408a200fc6d47381
MD5 c2c492211a9b80707f49163e7fb06c5e
BLAKE2b-256 2f92105b3366432f4389165e4fb0eae4be2b95a1a650cbe49d0d960fdd9d7013

See more details on using hashes here.

Provenance

The following attestation bundles were made for py_logguard-0.3.2-py3-none-any.whl:

Publisher: release.yml on DMsuDev/logguard

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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