Lightweight and flexible logging library with structured logging support for Python
Project description
๐ 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
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5afac6c4690b5791acdfbec3b7fff6d2b64f39b0ec7f3453b9e0236b59dffadd
|
|
| MD5 |
c2ed4b62de14f33edc3f8d8a5a8afcc1
|
|
| BLAKE2b-256 |
e8ad851112dc3a1f7e6fb8da878b65c77d99b07c41fd176c0fa145c830cb2174
|
Provenance
The following attestation bundles were made for miraveja_log-0.1.0.tar.gz:
Publisher:
publish.yml on JomarJunior/miraveja-log
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
miraveja_log-0.1.0.tar.gz -
Subject digest:
5afac6c4690b5791acdfbec3b7fff6d2b64f39b0ec7f3453b9e0236b59dffadd - Sigstore transparency entry: 712548502
- Sigstore integration time:
-
Permalink:
JomarJunior/miraveja-log@066b850ed0a9cbe69531e536340beb73d7e26410 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/JomarJunior
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@066b850ed0a9cbe69531e536340beb73d7e26410 -
Trigger Event:
release
-
Statement type:
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e4a47080a4ee49f9cd64ebdf45c16b75adf0e20baca615747881d4b124a225a2
|
|
| MD5 |
45114b7bda959c2090418d5d90d12e94
|
|
| BLAKE2b-256 |
f90b05f83e44de2ac83d44690fea73fa7cd888eebd2f39fa1f3b2a10ac5b70f0
|
Provenance
The following attestation bundles were made for miraveja_log-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on JomarJunior/miraveja-log
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
miraveja_log-0.1.0-py3-none-any.whl -
Subject digest:
e4a47080a4ee49f9cd64ebdf45c16b75adf0e20baca615747881d4b124a225a2 - Sigstore transparency entry: 712548511
- Sigstore integration time:
-
Permalink:
JomarJunior/miraveja-log@066b850ed0a9cbe69531e536340beb73d7e26410 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/JomarJunior
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@066b850ed0a9cbe69531e536340beb73d7e26410 -
Trigger Event:
release
-
Statement type: