Skip to main content

Lightweight and flexible logging library with structured logging support for Python

Project description

๐Ÿ“ miraveja-log

Python Version License Code Style Status Coverage CI

A lightweight and flexible logging library with structured logging support for Python

Etymology: Combining "logging" with the Miraveja ecosystem naming convention

๐Ÿš€ Overview

miraveja-log is a modern logging library that provides a clean, configurable interface for application logging. Built with DDD/Hexagonal Architecture principles, it offers flexible output targets, environment-based configuration, and structured logging capabilities.

Part of the Miraveja ecosystem, miraveja-log provides logging infrastructure for all ecosystem services.

โœจ Key Features

  • ๐ŸŽฏ Clean Interface - Simple, intuitive ILogger interface for all logging needs
  • ๐Ÿ“Š Multiple Targets - Console, file, and JSON output support
  • โš™๏ธ Environment Config - Load configuration from environment variables
  • ๐Ÿ—๏ธ Factory Pattern - Easy logger creation with sensible defaults
  • ๐Ÿ”„ Structured Logging - Support for contextual log data
  • ๐Ÿงช Testing Utilities - Built-in mocking and testing capabilities
  • ๐Ÿ›๏ธ Clean Architecture - Organized following DDD/Hexagonal Architecture principles

๐Ÿ› ๏ธ Technology Stack

๐Ÿ Core Runtime

  • Python 3.10+ - Type hints and modern Python features
  • typing-extensions - Compatibility for Python 3.8-3.9
  • pydantic - Configuration validation and modeling

๐Ÿงช Development

  • pytest - Testing framework with async support
  • pytest-asyncio - Async testing utilities
  • pytest-cov - Coverage reporting
  • black - Code formatter
  • pylint - Code quality checker
  • isort - Import statement organizer
  • pre-commit - Git hook framework for automated checks

๐Ÿ›๏ธ Architecture

miraveja-log follows Domain-Driven Design and Hexagonal Architecture principles:

src/miraveja_log/
โ”œโ”€โ”€ ๐Ÿง  domain/           # Core business logic (models, enums, interfaces, exceptions)
โ”œโ”€โ”€ ๐ŸŽฌ application/      # Use cases (configuration, factory)
โ””โ”€โ”€ ๐Ÿ”Œ infrastructure/   # External integrations (future: FastAPI, Django)

Dependency Rule: Domain โ† Application โ† Infrastructure

  • Domain has no dependencies on other layers
  • Application depends only on Domain
  • Infrastructure depends on Application and Domain

๐ŸŽฏ Getting Started

๐Ÿ“‹ Prerequisites

  • Python 3.10+
  • Poetry 2.0+ (recommended) or pip

๐Ÿš€ Installation

poetry add miraveja-log

Or with pip:

pip install miraveja-log

๐Ÿ“– Quick Start

Basic Usage

from miraveja_log import LoggerFactory, LoggerConfig, LogLevel, OutputTarget
from miraveja_log.infrastructure.adapters import PythonLoggerAdapter, AsyncPythonLoggerAdapter

# Create a simple console logger
factory = LoggerFactory(PythonLoggerAdapter, AsyncPythonLoggerAdapter)
config = LoggerConfig(name="my_app")
logger = factory.get_or_create_logger(config)

# Log messages at different levels
logger.info("Application started")
logger.debug("Debug information")
logger.warning("Warning message")
logger.error("Error occurred")
logger.critical("Critical system failure")

File Logging

from pathlib import Path
from miraveja_log import LoggerFactory, LoggerConfig, LogLevel, OutputTarget
from miraveja_log.infrastructure.adapters import PythonLoggerAdapter, AsyncPythonLoggerAdapter

# Create a file logger with custom configuration
factory = LoggerFactory(PythonLoggerAdapter, AsyncPythonLoggerAdapter)
config = LoggerConfig(
    name="my_app",
    level=LogLevel.DEBUG,
    output_target=OutputTarget.FILE,
    directory=Path("./logs"),
    filename="app.log",
    log_format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
    date_format="%Y-%m-%d %H:%M:%S"
)
logger = factory.get_or_create_logger(config)

logger.info("This will be written to the log file")

JSON Logging with Structured Data

from pathlib import Path
from miraveja_log import LoggerFactory, LoggerConfig, OutputTarget
from miraveja_log.infrastructure.adapters import PythonLoggerAdapter, AsyncPythonLoggerAdapter

# Create a JSON logger for structured logging
factory = LoggerFactory(PythonLoggerAdapter, AsyncPythonLoggerAdapter)
config = LoggerConfig(
    name="my_app",
    output_target=OutputTarget.JSON,
    directory=Path("./logs"),
    filename="app.json"
)
logger = factory.get_or_create_logger(config)

# Log with structured data - extra fields are merged at top level
logger.info(
    "User logged in",
    extra={
        "user_id": 12345,
        "username": "john_doe",
        "ip_address": "192.168.1.1"
    }
)
# JSON output: {"timestamp": "2025-11-21T...", "level": "INFO", "name": "my_app",
#               "message": "User logged in", "user_id": 12345, "username": "john_doe", ...}

Environment-Based Configuration

import os
from miraveja_log import LoggerFactory, LoggerConfig
from miraveja_log.infrastructure.adapters import PythonLoggerAdapter, AsyncPythonLoggerAdapter

# Set environment variables
os.environ["LOGGER_NAME"] = "my_service"
os.environ["LOGGER_LEVEL"] = "INFO"
os.environ["LOGGER_TARGET"] = "FILE"
os.environ["LOGGER_DIR"] = "./logs"
os.environ["LOGGER_FILENAME"] = "service.log"

# Load configuration from environment
config = LoggerConfig.from_env()
factory = LoggerFactory(PythonLoggerAdapter, AsyncPythonLoggerAdapter)
logger = factory.get_or_create_logger(config)

logger.info("Configured from environment variables")

Async Logging

import asyncio
from miraveja_log import LoggerFactory, LoggerConfig
from miraveja_log.infrastructure.adapters import PythonLoggerAdapter, AsyncPythonLoggerAdapter

async def main():
    # Create an async logger
    factory = LoggerFactory(PythonLoggerAdapter, AsyncPythonLoggerAdapter)
    config = LoggerConfig(name="async_app")
    logger = factory.get_or_create_async_logger(config)

    # Use async logging methods
    await logger.info("Async operation started")
    await logger.debug("Processing data asynchronously")
    await logger.info("Async operation completed")

asyncio.run(main())

Exception Logging

from miraveja_log import LoggerFactory, LoggerConfig
from miraveja_log.infrastructure.adapters import PythonLoggerAdapter, AsyncPythonLoggerAdapter

factory = LoggerFactory(PythonLoggerAdapter, AsyncPythonLoggerAdapter)
logger = factory.get_or_create_logger(LoggerConfig(name="my_app"))

# Log exceptions with traceback
try:
    result = 10 / 0
except ZeroDivisionError:
    logger.error("Division by zero error", exc_info=True)

๐Ÿ”ง Configuration

Logger Levels

miraveja-log supports standard Python logging levels:

  • LogLevel.DEBUG - Detailed information for diagnosing problems (default)
  • LogLevel.INFO - Confirmation that things are working as expected
  • LogLevel.WARNING - Indication of potential problems
  • LogLevel.ERROR - Error that caused functionality to fail
  • LogLevel.CRITICAL - Serious error that may cause the program to abort

Output Targets

  • OutputTarget.CONSOLE - Output to console/stdout (default)
  • OutputTarget.FILE - Output to a file with text formatting
  • OutputTarget.JSON - Output to a file in JSON format (structured logging)

Environment Variables

Configure your logger using environment variables:

# Required
LOGGER_NAME=my_service          # Logger name (default: "default_name")
LOGGER_LEVEL=INFO               # Logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
LOGGER_TARGET=CONSOLE           # Output target (CONSOLE, FILE, JSON)

# Required for FILE/JSON targets
LOGGER_DIR=./logs               # Directory for log files (resolved to absolute path)
LOGGER_FILENAME=app.log         # Log file name

# Optional formatting
LOGGER_FORMAT=%(asctime)s - %(name)s - %(levelname)s - %(message)s
LOGGER_DATEFMT=%Y-%m-%d %H:%M:%S

Configuration Model

from pathlib import Path
from miraveja_log import LoggerConfig, LogLevel, OutputTarget

config = LoggerConfig(
    name="my_service",
    level=LogLevel.INFO,
    output_target=OutputTarget.CONSOLE,
    log_format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
    date_format="%Y-%m-%d %H:%M:%S",
    directory=None,  # Required for FILE/JSON targets
    filename=None    # Required for FILE/JSON targets
)

# Use with factory
from miraveja_log import LoggerFactory
from miraveja_log.infrastructure.adapters import PythonLoggerAdapter, AsyncPythonLoggerAdapter

factory = LoggerFactory(PythonLoggerAdapter, AsyncPythonLoggerAdapter)
logger = factory.get_or_create_logger(config)

๐Ÿ“š API Reference

LoggerFactory

Factory class for creating and caching logger instances.

Constructor:

LoggerFactory(sync_adapter_class, async_adapter_class)
  • sync_adapter_class: Class for synchronous logger adapter (e.g., PythonLoggerAdapter)
  • async_adapter_class: Class for asynchronous logger adapter (e.g., AsyncPythonLoggerAdapter)

Methods:

  • get_or_create_logger(config: LoggerConfig) -> ILogger

    • Creates or retrieves cached synchronous logger instance
    • Returns: Configured ILogger instance
    • Thread-safe with internal locking
  • get_or_create_async_logger(config: LoggerConfig) -> IAsyncLogger

    • Creates or retrieves cached asynchronous logger instance
    • Returns: Configured IAsyncLogger instance
    • Thread-safe with internal locking
  • clear_cache() -> None

    • Clears all cached logger instances (both sync and async)

ILogger Interface

Abstract interface for synchronous logging operations.

Methods:

  • debug(msg, *args, **kwargs) - Log debug message
  • info(msg, *args, **kwargs) - Log info message
  • warning(msg, *args, **kwargs) - Log warning message
  • error(msg, *args, **kwargs) - Log error message
  • critical(msg, *args, **kwargs) - Log critical message

Parameters:

  • msg: Log message (supports string formatting with args)
  • *args: Positional arguments for message formatting
  • **kwargs: Keyword arguments including:
    • extra: Dict of extra fields for structured logging
    • exc_info: Include exception traceback (True/False/tuple)
    • stack_info: Include stack trace information
    • stacklevel: Stack level for correct caller info

IAsyncLogger Interface

Abstract interface for asynchronous logging operations.

Methods:

All methods are async and mirror ILogger interface:

  • async debug(msg, *args, **kwargs) - Log debug message asynchronously
  • async info(msg, *args, **kwargs) - Log info message asynchronously
  • async warning(msg, *args, **kwargs) - Log warning message asynchronously
  • async error(msg, *args, **kwargs) - Log error message asynchronously
  • async critical(msg, *args, **kwargs) - Log critical message asynchronously

LoggerConfig

Pydantic model for logger configuration with validation.

Fields:

  • name: str - Logger name (required)
  • level: LogLevel - Logging level (default: DEBUG)
  • output_target: OutputTarget - Output target (default: CONSOLE)
  • log_format: Optional[str] - Log format string (default: standard format)
  • date_format: Optional[str] - Date format string (default: '%Y-%m-%d %H:%M:%S')
  • directory: Optional[Path] - Directory for log files (required for FILE/JSON)
  • filename: Optional[str] - Log filename (required for FILE/JSON)

Class Methods:

  • from_env() -> LoggerConfig
    • Creates configuration from environment variables
    • Returns: LoggerConfig instance
    • Validates required fields based on output target
    • Resolves relative paths to absolute paths

๐Ÿ”ฅ Advanced Usage

Logger Caching

The factory automatically caches logger instances by name for both sync and async loggers:

from miraveja_log import LoggerFactory, LoggerConfig
from miraveja_log.infrastructure.adapters import PythonLoggerAdapter, AsyncPythonLoggerAdapter

factory = LoggerFactory(PythonLoggerAdapter, AsyncPythonLoggerAdapter)
config = LoggerConfig(name="cached_logger")

# First call creates the logger
logger1 = factory.get_or_create_logger(config)

# Second call returns the same cached instance
logger2 = factory.get_or_create_logger(config)

assert logger1 is logger2  # Same instance

# Clear cache if needed
factory.clear_cache()

Structured Logging with JSON Output

JSON output automatically merges extra fields at the top level for easy parsing:

from pathlib import Path
from miraveja_log import LoggerFactory, LoggerConfig, OutputTarget
from miraveja_log.infrastructure.adapters import PythonLoggerAdapter, AsyncPythonLoggerAdapter

factory = LoggerFactory(PythonLoggerAdapter, AsyncPythonLoggerAdapter)
config = LoggerConfig(
    name="api_logger",
    output_target=OutputTarget.JSON,
    directory=Path("./logs"),
    filename="api.json"
)
logger = factory.get_or_create_logger(config)

# Log API request with structured data
logger.info(
    "API request processed",
    extra={
        "method": "POST",
        "endpoint": "/api/users",
        "status_code": 201,
        "response_time_ms": 45,
        "user_agent": "Mozilla/5.0..."
    }
)

# Output (each line is valid JSON):
# {"timestamp": "2025-11-21T10:30:45.123456", "level": "INFO", "name": "api_logger",
#  "message": "API request processed", "method": "POST", "endpoint": "/api/users",
#  "status_code": 201, "response_time_ms": 45, "user_agent": "Mozilla/5.0..."}

Multiple Loggers for Different Purposes

from pathlib import Path
from miraveja_log import LoggerFactory, LoggerConfig, LogLevel, OutputTarget
from miraveja_log.infrastructure.adapters import PythonLoggerAdapter, AsyncPythonLoggerAdapter

factory = LoggerFactory(PythonLoggerAdapter, AsyncPythonLoggerAdapter)

# Application logger - console output
app_logger = factory.get_or_create_logger(
    LoggerConfig(name="app", level=LogLevel.INFO)
)

# Error logger - file output
error_logger = factory.get_or_create_logger(
    LoggerConfig(
        name="errors",
        level=LogLevel.ERROR,
        output_target=OutputTarget.FILE,
        directory=Path("./logs"),
        filename="errors.log"
    )
)

# Audit logger - JSON output
audit_logger = factory.get_or_create_logger(
    LoggerConfig(
        name="audit",
        output_target=OutputTarget.JSON,
        directory=Path("./logs"),
        filename="audit.json"
    )
)

app_logger.info("Application started")
error_logger.error("Critical error occurred")
audit_logger.info("User action", extra={"user_id": 123, "action": "login"})

Exception Handling with Async Logging

import asyncio
from miraveja_log import LoggerFactory, LoggerConfig
from miraveja_log.infrastructure.adapters import PythonLoggerAdapter, AsyncPythonLoggerAdapter

async def process_data():
    factory = LoggerFactory(PythonLoggerAdapter, AsyncPythonLoggerAdapter)
    logger = factory.get_or_create_async_logger(LoggerConfig(name="async_app"))

    try:
        await logger.info("Starting data processing")
        # Simulate error
        result = 1 / 0
    except ZeroDivisionError:
        # Exception context is preserved even in async logging
        await logger.error("Processing failed", exc_info=True)
    finally:
        await logger.info("Cleanup completed")

asyncio.run(process_data())

Custom Format Strings

from pathlib import Path
from miraveja_log import LoggerFactory, LoggerConfig, OutputTarget
from miraveja_log.infrastructure.adapters import PythonLoggerAdapter, AsyncPythonLoggerAdapter

factory = LoggerFactory(PythonLoggerAdapter, AsyncPythonLoggerAdapter)

# Minimal format
minimal_logger = factory.get_or_create_logger(
    LoggerConfig(
        name="minimal",
        log_format="%(levelname)s: %(message)s"
    )
)

# Detailed format with module and function info
detailed_logger = factory.get_or_create_logger(
    LoggerConfig(
        name="detailed",
        log_format="%(asctime)s [%(levelname)-8s] %(name)s.%(funcName)s:%(lineno)d - %(message)s",
        date_format="%Y-%m-%d %H:%M:%S"
    )
)

minimal_logger.info("Simple message")
# Output: INFO: Simple message

detailed_logger.info("Detailed message")
# Output: 2025-11-21 10:30:45 [INFO    ] detailed.my_function:42 - Detailed message

๐Ÿ“‚ Examples

Complete working examples are available in the examples/ directory:

  • basic_usage.py - Console, file, and JSON logging examples
  • async_usage.py - Async logging with concurrent tasks

Run examples:

# Basic examples
poetry run python examples/basic_usage.py

# Async examples
poetry run python examples/async_usage.py

๐Ÿงช Testing

Running Tests

# Run all tests
poetry run pytest

# Run with coverage
poetry run pytest --cov=src/miraveja_log --cov-report=html

# Run specific test file
poetry run pytest tests/unit/miraveja_log/application/test_configuration.py

# Run with verbose output
poetry run pytest -v

# Run integration tests only
poetry run pytest tests/integration

# Run unit tests only
poetry run pytest tests/unit

Test Structure

tests/
โ”œโ”€โ”€ unit/                      # Unit tests
โ”‚   โ””โ”€โ”€ miraveja_log/
โ”‚       โ”œโ”€โ”€ domain/           # Domain layer tests
โ”‚       โ””โ”€โ”€ application/      # Application layer tests
โ””โ”€โ”€ integration/              # Integration tests
    โ””โ”€โ”€ miraveja_log/

๐Ÿค Contributing

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

Development Setup

# Clone the repository
git clone https://github.com/JomarJunior/miraveja-log.git
cd miraveja-log

# Install dependencies
poetry install

# Install pre-commit hooks
poetry run pre-commit install

# Run tests
poetry run pytest --cov=src/miraveja_log

Code Quality

# Format code
poetry run black src tests

# Sort imports
poetry run isort src tests

# Run linter
poetry run pylint src/miraveja_log

# Run type checker
poetry run mypy src/miraveja_log

๐Ÿ“Š Development Status

miraveja-log is actively developed and maintained. Current status: Beta

Roadmap

  • โœ… Core logging functionality
  • โœ… Multiple output targets (console, file, JSON)
  • โœ… Environment-based configuration
  • โœ… Structured logging support (extra fields merged at top level)
  • โœ… Async logging support
  • โœ… Logger caching and factory pattern
  • โœ… Thread-safe operations
  • โœ… Exception context preservation in async logging
  • ๐Ÿšง FastAPI integration
  • ๐Ÿšง Django integration
  • ๐Ÿšง Log rotation and archiving
  • ๐Ÿšง Cloud logging integration (AWS CloudWatch, Google Cloud Logging)
  • ๐Ÿšง Sensitive data filtering

๐Ÿ“„ License

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

๐Ÿ™ Acknowledgments

  • Built as part of the Miraveja ecosystem
  • Inspired by Python's standard logging module
  • Follows DDD/Hexagonal Architecture principles

๐Ÿ“ž Contact

๐Ÿ”— Related Projects


Made with โค๏ธ for the Miraveja ecosystem

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

miraveja_log-0.1.0.tar.gz (18.3 kB view details)

Uploaded Source

Built Distribution

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

miraveja_log-0.1.0-py3-none-any.whl (18.6 kB view details)

Uploaded Python 3

File details

Details for the file miraveja_log-0.1.0.tar.gz.

File metadata

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

File hashes

Hashes for miraveja_log-0.1.0.tar.gz
Algorithm Hash digest
SHA256 5afac6c4690b5791acdfbec3b7fff6d2b64f39b0ec7f3453b9e0236b59dffadd
MD5 c2ed4b62de14f33edc3f8d8a5a8afcc1
BLAKE2b-256 e8ad851112dc3a1f7e6fb8da878b65c77d99b07c41fd176c0fa145c830cb2174

See more details on using hashes here.

Provenance

The following attestation bundles were made for miraveja_log-0.1.0.tar.gz:

Publisher: publish.yml on JomarJunior/miraveja-log

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

File details

Details for the file miraveja_log-0.1.0-py3-none-any.whl.

File metadata

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

File hashes

Hashes for miraveja_log-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 e4a47080a4ee49f9cd64ebdf45c16b75adf0e20baca615747881d4b124a225a2
MD5 45114b7bda959c2090418d5d90d12e94
BLAKE2b-256 f90b05f83e44de2ac83d44690fea73fa7cd888eebd2f39fa1f3b2a10ac5b70f0

See more details on using hashes here.

Provenance

The following attestation bundles were made for miraveja_log-0.1.0-py3-none-any.whl:

Publisher: publish.yml on JomarJunior/miraveja-log

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