Skip to main content

📝 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

Metadata

Release files for miraveja-log 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for miraveja-log 0.1.0
File Size Uploaded
miraveja_log-0.1.0.tar.gz 18.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for miraveja-log 0.1.0
File Interpreter ABI Platform
miraveja_log-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 37.0 kB

Release files / miraveja_log-0.1.0.tar.gz

Download URL miraveja_log-0.1.0.tar.gz
Size 18.3 kB
Tags Source
SHA-256 checksum
How to use checksums
5afac6c4690b5791acdfbec3b7fff6d2b64f39b0ec7f3453b9e0236b59dffadd
BLAKE2b-256 checksum
How to use checksums
e8ad851112dc3a1f7e6fb8da878b65c77d99b07c41fd176c0fa145c830cb2174
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 21, 2025.

Transparency log

Release files / miraveja_log-0.1.0-py3-none-any.whl

Download URL miraveja_log-0.1.0-py3-none-any.whl
Size 18.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e4a47080a4ee49f9cd64ebdf45c16b75adf0e20baca615747881d4b124a225a2
BLAKE2b-256 checksum
How to use checksums
f90b05f83e44de2ac83d44690fea73fa7cd888eebd2f39fa1f3b2a10ac5b70f0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 21, 2025.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page