📝 miraveja-log
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 expectedLogLevel.WARNING- Indication of potential problemsLogLevel.ERROR- Error that caused functionality to failLogLevel.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 formattingOutputTarget.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 messageinfo(msg, *args, **kwargs)- Log info messagewarning(msg, *args, **kwargs)- Log warning messageerror(msg, *args, **kwargs)- Log error messagecritical(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 loggingexc_info: Include exception traceback (True/False/tuple)stack_info: Include stack trace informationstacklevel: 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 asynchronouslyasync info(msg, *args, **kwargs)- Log info message asynchronouslyasync warning(msg, *args, **kwargs)- Log warning message asynchronouslyasync error(msg, *args, **kwargs)- Log error message asynchronouslyasync 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 examplesasync_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
- Author: Jomar Júnior de Souza Pereira
- Email: jomarjunior@poli.ufrj.br
- Repository: https://github.com/JomarJunior/miraveja-log
🔗 Related Projects
- miraveja-di - Dependency Injection container
- miraveja - Main Miraveja project
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)
| File | Size | Uploaded | |
|---|---|---|---|
| miraveja_log-0.1.0.tar.gz | 18.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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 logRelease 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