A logger based on structlog
Project description
risclog.logging
risclog.logging is a comprehensive structured logging solution for Python applications. It combines Python’s built-in logging module with structlog to provide powerful, flexible logging with support for both synchronous and asynchronous code.
Key Features:
`getLogger` – Modern factory function for creating logger instances (legacy get_logger deprecated)
`log_decorator` – Automatic function logging with execution time, arguments, return values, and exception handling
Structured logging – Rich, contextual log entries with automatic JSON serialization
Async/Sync support – Unified API for both synchronous and asynchronous code
File & Console output – Flexible handler configuration
Email notifications – Optional exception alerts via SMTP (with email configuration)
Enhanced tracebacks – Beautiful, colored exception display via Rich
Features
Structured logging: Combines standard logging with structlog for rich, contextual logs
Synchronous and asynchronous logging: Unified API works seamlessly in both environments
Automatic function logging: Use @log_decorator to automatically capture function execution details:
Function arguments and their types
Return values
Execution duration (in milliseconds)
Exception stack traces and details
Email notifications: Optionally send SMTP email alerts when decorated functions raise exceptions
Rich tracebacks: Enhanced exception display with colors and source code context
Flexible configuration:
Set log levels per logger
Add file handlers with custom formatters
Configure via environment variables (LOG_LEVEL, LOG_EXCLUDED_LOGGERS, etc.)
Auto-filters verbose library logs (uvicorn, watchfiles, etc.)
Production-ready: Type hints, comprehensive tests, and battle-tested in production applications
Installation
Install via pip:
pip install risclog.logging
Configuration and Usage
Quick Start
from risclog.logging import getLogger
logger = getLogger(__name__)
logger.set_level("DEBUG")
# Log messages
logger.info("Application started")
logger.warning("Something might be wrong", user_id=42)
logger.error("An error occurred", error_code=500)
Creating a Logger
Use the getLogger function to obtain a logger instance:
from risclog.logging import getLogger
logger = getLogger(__name__)
# Set log level (accepts string or logging constant)
logger.set_level("DEBUG") # or logging.DEBUG
# Add file handler
logger.add_file_handler('app.log', level=logging.DEBUG)
Logging Messages
Synchronous logging:
logger.debug("Debug message")
logger.info("Info message", user_id=42, action="login")
logger.warning("Warning message", retry_count=3)
logger.error("Error message", error_code=500)
logger.critical("Critical failure", severity="high")
Asynchronous logging:
await logger.debug("Async debug message")
await logger.info("Async info message", user_id=42)
# All log methods support both sync and async calls
Automatic Function Logging with Decorators
The @log_decorator automatically logs function execution with comprehensive details:
Synchronous functions:
from risclog.logging import log_decorator
@log_decorator
def calculate_sum(a: int, b: int) -> int:
"""Calculate sum of two numbers."""
result = a + b
return result
result = calculate_sum(10, 20)
# Logs: [Decorator start: calculate_sum]
# Logs: [Decorator success: calculate_sum] duration=0.00123sec result=30
Asynchronous functions:
@log_decorator
async def fetch_data(user_id: int) -> dict:
"""Fetch user data from API."""
await asyncio.sleep(1) # Simulate API call
return {"id": user_id, "name": "Alice"}
data = await fetch_data(123)
# Logs: [Decorator start: fetch_data] args=('user_id:int=123',)
# Logs: [Decorator success: fetch_data] duration=1.00234sec result={'id': 123, 'name': 'Alice'}
Error handling with decorator:
@log_decorator
def risky_operation(value: int) -> float:
"""Operation that might fail."""
return 100 / value # Will raise ZeroDivisionError if value=0
try:
risky_operation(0)
except ZeroDivisionError:
pass
# Logs: [Decorator error in risky_operation] error='division by zero'
Using the Decorator in Classes
Use @log_decorator on class methods:
from risclog.logging import getLogger, log_decorator
class UserService:
def __init__(self):
self.logger = getLogger(__name__)
@log_decorator
def get_user(self, user_id: int) -> dict:
"""Retrieve user by ID."""
self.logger.info("Fetching user", user_id=user_id)
# Simulate database lookup
return {"id": user_id, "name": "John Doe", "email": "john@example.com"}
@log_decorator
async def update_user_async(self, user_id: int, name: str) -> dict:
"""Update user asynchronously."""
await self.logger.info("Updating user", user_id=user_id, name=name)
await asyncio.sleep(0.5) # Simulate API call
return {"id": user_id, "name": name, "updated": True}
# Usage
service = UserService()
user = service.get_user(42)
# Logs: [Decorator start: get_user] args=('self:UserService=...', 'user_id:int=42')
# Logs: Fetching user (manual log)
# Logs: [Decorator success: get_user] result={'id': 42, 'name': 'John Doe', 'email': 'john@example.com'}
Email Notification on Exceptions
To send email alerts when a decorated function raises an exception, use send_email=True.
Required environment variables:
export logging_email_smtp_user="your-email@gmail.com"
export logging_email_smtp_password="your-app-password"
export logging_email_smtp_server="smtp.gmail.com"
export logging_email_to="admin@example.com"
Usage:
@log_decorator(send_email=True)
def critical_operation():
"""This function will send email on exception."""
# Your code
...
When an exception occurs, the logger will automatically send an email notification with the full error traceback.
Rich Traceback Integration
The package uses Rich for beautiful exception display. Rich tracebacks include syntax highlighting, source code context, and improved readability:
from rich import traceback
traceback.install() # Install the Rich traceback handler
# Now all exceptions will be displayed with Rich formatting
1 / 0 # Beautiful traceback with colors and context
Full Example
Here’s a complete example demonstrating logging with both sync and async functions:
import asyncio
import logging
from risclog.logging import getLogger, log_decorator
# Configure logger
logger = getLogger(__name__)
logger.set_level(logging.DEBUG)
logger.add_file_handler('app.log', level=logging.DEBUG)
# Simple sync function
@log_decorator
def calculate(a: int, b: int) -> int:
logger.debug("Performing calculation", a=a, b=b)
result = a + b
return result
# Simple async function
@log_decorator
async def fetch_user(user_id: int) -> dict:
await logger.info("Fetching user from API", user_id=user_id)
await asyncio.sleep(1) # Simulate API latency
return {"id": user_id, "name": "John"}
# Class-based logging
class DataProcessor:
def __init__(self):
self.logger = getLogger(__name__)
@log_decorator
def process(self, data: list) -> int:
self.logger.info("Processing data", count=len(data))
total = sum(data)
self.logger.info("Processing complete", total=total)
return total
@log_decorator
async def process_async(self, data: list) -> int:
await self.logger.info("Async processing", count=len(data))
await asyncio.sleep(0.5)
total = sum(data)
return total
# Main execution
async def main():
# Sync calls
logger.info("Application started")
result = calculate(5, 10)
logger.info("Calculation result", result=result)
# Async calls
user = await fetch_user(123)
logger.info("User fetched", user=user)
# Class-based calls
processor = DataProcessor()
total = processor.process([1, 2, 3, 4, 5])
logger.info("Processing complete", total=total)
total_async = await processor.process_async([10, 20, 30])
logger.info("Async processing complete", total=total_async)
if __name__ == "__main__":
asyncio.run(main())
Example Output
When running the example above, you’ll see output similar to this:
2026-01-16 12:16:13 [info] Application started
2026-01-16 12:16:13 [info] [4301639536 Decorator start: calculate] _function=calculate _script=example.py args=('a:int=5', 'b:int=10') kwargs={}
2026-01-16 12:16:13 [debug] Performing calculation a=5 b=10
2026-01-16 12:16:13 [info] [4301639536 Decorator success: calculate] _function=calculate result=15 duration=0.00123sec
2026-01-16 12:16:13 [info] Calculation result result=15
2026-01-16 12:16:13 [info] [4311754480 Decorator start: fetch_user] _function=fetch_user args=('user_id:int=123',)
2026-01-16 12:16:13 [info] Fetching user from API user_id=123
2026-01-16 12:16:14 [info] [4311754480 Decorator success: fetch_user] _function=fetch_user result={'id': 123, 'name': 'John'} duration=1.00234sec
2026-01-16 12:16:14 [info] User fetched user={'id': 123, 'name': 'John'}
2026-01-16 12:16:14 [info] [4312228144 Decorator start: process] _function=process args=('self:DataProcessor=...', 'data:list=[1, 2, 3, 4, 5]')
2026-01-16 12:16:14 [info] Processing data count=5
2026-01-16 12:16:14 [info] Processing complete total=15
2026-01-16 12:16:14 [info] [4312228144 Decorator success: process] _function=process result=15 duration=0.00098sec
2026-01-16 12:16:14 [info] Processing complete total=15
Running Tests
To run the tests for this package, execute:
pytest
Or with verbose output:
pytest -v
Development
To set up a development environment, clone the repository and install in editable mode:
git clone https://github.com/risclog-solution/risclog.logging
cd risclog.logging
pip install -e .[dev]
pytest
Troubleshooting
Logs not showing in console:
If you see logs written to files but not in the console, ensure you’ve added a console handler:
import logging
# Add console output
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s [%(levelname)s] %(message)s')
console_handler.setFormatter(formatter)
logging.getLogger().addHandler(console_handler)
Email notifications not working:
Verify all required environment variables are set:
echo $logging_email_smtp_user
echo $logging_email_smtp_password
echo $logging_email_smtp_server
echo $logging_email_to
Performance considerations:
The @log_decorator adds minimal overhead (typically < 1ms)
File I/O is buffered by Python’s logging module
For high-throughput applications, consider using async logging
Migration from Old API
If you’re using the old get_logger function (deprecated), migrate to the new getLogger:
Old code:
from risclog.logging import get_logger # Deprecated
logger = get_logger(__name__)
New code:
from risclog.logging import getLogger # Current
logger = getLogger(__name__)
The functionality is the same, but getLogger is the recommended approach.
Credits
This package was created using Cookiecutter and the risclog-solution/risclog-cookiecutter-pypackage project template.
License
This project is licensed under the MIT License. See the LICENSE file for details.
Contributing
Contributions are welcome! Please see CONTRIBUTING.rst for guidelines.
Support
For issues, questions, or feature requests, please open an issue on GitHub.
Changelog
See CHANGES.rst for version history and updates.
Change log for risclog.logging
2.2.0 (2026-01-29)
Make log_decorator a no-op unless the logger level is DEBUG.
Honor retry_delay for SMTP retry timing.
2.1.0 (2026-01-27)
Use JSONRenderer for logging to journal so that log entries are structured and can be parsed by log management systems.
2.0.0 (2026-01-19)
Add comprehensive type hints to core logging modules for better IDE support and type safety
Improve mypy configuration to exclude examples, scripts, and docs folders
Update and expand README.rst with detailed usage examples, troubleshooting guide, and migration instructions
Add executable example scripts (test_logger.py, api.py) demonstrating logger functionality
Fix RST formatting issues (backticks) in CHANGES.rst and CONTRIBUTING.rst
Enhance .pre-commit-config.yaml to properly exclude non-source files from mypy checks
1.3.3 (2025-12-04)
fix: wrapper async
1.3.2 (2025-10-15)
remove pretty logging with rich
1.3.1 (2025-09-08)
Unify default log level and set it from DEBUG to WARNING.
1.3.0 (2025-02-06)
added add_file_handler method to add a file handler to a logger
added set_level method to set the level of a logger
Fix log decorator mixed async sync Problem
old decorator function is now deprecated
old get_logger function is now deprecated
1.2.1 (2024-09-20)
forward exceptions
1.2.0 (2024-09-19)
rename email environments
1.1.0 (2024-08-27)
Allow logging with multiple loggers in a single module.
1.0.2 (2024-08-06)
Fix CI status badge.
1.0.1 (2024-08-06)
Fix classifiers and README structure.
1.0 (2024-08-06)
initial release
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
File details
Details for the file risclog_logging-2.2.0.tar.gz.
File metadata
- Download URL: risclog_logging-2.2.0.tar.gz
- Upload date:
- Size: 32.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.0.1 CPython/3.10.16
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
837768579f2b2d1fb42bcd9ef93a3774da75e195a82fef75b791e808747605ee
|
|
| MD5 |
6e5de1b7fbcc2f5b4e7ca1d2c5fa6dbc
|
|
| BLAKE2b-256 |
8f3f7cab0fddd3cda181644f44d2b7951948ee77076b4ce74e4637a1987dd039
|