Asynchronous logging library mirroring the standard logging API, with queue-based non-blocking handlers
Project description
aiologging
Asynchronous logging library for Python (3.9–3.14). The API mirrors the standard logging module — same method names, signatures, hierarchy and semantics — with the logging methods being coroutines. Records are created at the call site and put on a queue; a background consumer performs the handler I/O, so await logger.info(...) never waits for a file write or an HTTP request.
Features
- logging-compatible API: same methods, levels, hierarchy, filters and
LogRecordsemantics as the standardloggingmodule - Background consumer: handler I/O happens off the calling coroutine's path; the consumer starts lazily and survives event loop changes
- Configurable delivery:
awaitresolves on enqueue (default) or after handlers processed the record (delivery="await") - Configurable backpressure: bounded queue with
block(default),drop_newordrop_oldoverflow policies - Stdlib bridge:
captureStdlib()routes third-party library logs (aiohttp, sqlalchemy, ...) through the same async handlers - Async Handlers: non-blocking I/O for streams, files (with size/time rotation) and HTTP endpoints with extensible authentication
- Buffered Handlers: batch processing for high-volume logging
- Performance Metrics: built-in metrics for loggers, handlers and the queue
- Strict Type Checking: full mypy support with type hints
Installation
Basic Installation
pip install aiologging
With Optional Dependencies
# For file handlers
pip install aiologging[aiofiles]
# For HTTP handlers
pip install aiologging[aiohttp]
# For Protobuf support
pip install aiologging[protobuf]
# All dependencies
pip install aiologging[all]
# Development dependencies
pip install aiologging[dev]
Quick Start
import asyncio
import aiologging
async def main():
aiologging.basicConfig(level=aiologging.INFO)
logger = aiologging.getLogger("app")
await logger.info("Application started")
await logger.warning("Something might be wrong")
await logger.error("An error occurred: %s", "details")
# once, before the loop goes away: drain the queue, close handlers
await aiologging.shutdown()
asyncio.run(main())
Module-level convenience functions work like in standard logging:
await aiologging.warning("Logged on the root logger")
How it works
await logger.info(...) synchronously creates the LogRecord (so caller info, %-formatting and exc_info behave exactly like standard logging) and puts it on a bounded queue. A background task drains the queue and dispatches records to the async handlers. By default the await resolves as soon as the record is enqueued — logging is nearly free for the calling coroutine.
Lifecycle:
- the consumer starts lazily on the first logged record and is rebuilt transparently if the event loop changes (e.g. one loop per test);
await aiologging.flush()waits until everything queued has been handled;await aiologging.shutdown()drains the queue and closes all handlers — call it once at application exit.
Delivery and backpressure
aiologging.basicConfig(
level=aiologging.INFO,
queue_size=10_000, # queue capacity
overflow="block", # "block" | "drop_new" | "drop_old"
delivery="enqueue", # "enqueue" | "await"
)
# per-logger override for critical logs: the await resolves only
# after the handlers have processed the record
audit = aiologging.getLogger("app.audit")
audit.delivery = "await"
overflow="block"(default): when the queue is full,await logger.info(...)waits for free space — no records are lost.overflow="drop_new"/"drop_old": the call never waits; the incoming or the oldest record is discarded and counted in metrics.
Capturing stdlib logging
Third-party libraries log via the standard logging module. Route their records through your async handlers:
aiologging.basicConfig(level=aiologging.INFO, capture_stdlib=True)
# or:
aiologging.captureStdlib()
Bridged records are routed through the aiologging hierarchy under the same logger name, so per-name handlers and propagation work as usual. The bridge is thread-safe and buffers records emitted before the event loop starts.
Examples
Complete runnable examples live in the examples/ directory:
- examples/basic_usage.py — levels,
basicConfig,exc_info, delivery modes,flush/shutdown - examples/stdlib_capture.py — routing third-party (stdlib
logging) records through async handlers, including from threads and before the loop starts - examples/file_logging.py — file handler, size- and time-based rotation
- examples/http_logging.py — JSON batches over HTTP with a custom authenticator (includes a local test collector)
- examples/config_usage.py — configuring loggers from a dictionary
Handlers
Stream Handler
import sys
import aiologging
async def main():
logger = aiologging.getLogger("app")
logger.addHandler(aiologging.AsyncStreamHandler(sys.stdout))
await logger.info("This goes to stdout")
await aiologging.shutdown()
File Handler (requires aiofiles)
import aiologging
async def main():
logger = aiologging.getLogger("app")
logger.addHandler(aiologging.AsyncFileHandler("app.log"))
await logger.info("This goes to app.log")
await aiologging.shutdown()
Rotating File Handler (requires aiofiles)
import aiologging
async def main():
logger = aiologging.getLogger("app")
# Size-based rotation
logger.addHandler(aiologging.AsyncRotatingFileHandler(
"app.log",
max_bytes=1024*1024, # 1MB
backup_count=5,
))
# Time-based rotation
logger.addHandler(aiologging.AsyncTimedRotatingFileHandler(
"timed.log",
when="midnight",
backup_count=7,
))
await logger.info("This will be rotated")
await aiologging.shutdown()
HTTP Handler (requires aiohttp)
import aiologging
async def main():
logger = aiologging.getLogger("app")
logger.addHandler(aiologging.AsyncHttpHandler(
"https://api.example.com/logs",
headers={"Authorization": "Bearer token"},
))
await logger.info("This will be sent via HTTP")
await aiologging.shutdown()
Custom Authentication
import aiologging
async def oauth_authenticator(session, request_data):
"""Custom OAuth authentication."""
token = await refresh_oauth_token()
return {"Authorization": f"Bearer {token}"}
http_handler = aiologging.AsyncHttpHandler(
"https://api.example.com/logs",
authenticator=oauth_authenticator,
)
HTTP Handler Formats
JSON Handler
json_handler = aiologging.AsyncHttpJsonHandler(
"https://api.example.com/logs"
)
Text Handler
text_handler = aiologging.AsyncHttpTextHandler(
"https://api.example.com/logs"
)
Protobuf Handler (requires protobuf)
proto_handler = aiologging.AsyncHttpProtoHandler(
"https://api.example.com/logs"
)
Universal Handler (auto-detect format)
universal_handler = aiologging.AsyncHttpHandler(
"https://api.example.com/logs",
format_type="application/json" # Optional: auto-detected if not specified
)
Advanced Usage
Custom Filters
import aiologging
class CustomFilter:
def filter(self, record):
return "important" in record.getMessage()
logger = aiologging.getLogger("app")
logger.addFilter(CustomFilter())
Custom Formatters
Formatters live on handlers, exactly like in standard logging:
import logging
import aiologging
handler = aiologging.AsyncStreamHandler()
handler.setFormatter(logging.Formatter(
"%(asctime)s [%(levelname)s] %(name)s: %(message)s"
))
aiologging.getLogger("app").addHandler(handler)
Error Handling
import aiologging
async def error_handler(record, exception):
"""Custom error handler for failed log operations."""
print(f"Failed to log {record.getMessage()}: {exception}")
handler = aiologging.AsyncStreamHandler()
handler.error_handler = error_handler
Performance Metrics
from aiologging.logger import _logger_manager
logger = aiologging.getLogger("app")
print(logger.get_metrics()) # per-logger counters
print(_logger_manager.get_metrics()) # queue length, dropped records
for handler in logger.handlers:
print(handler.get_metrics()) # per-handler counters
Batch Processing
import aiologging
from aiologging.types import BatchConfig
http_handler = aiologging.AsyncHttpHandler(
"https://api.example.com/logs",
batch_config=BatchConfig(
batch_size=100,
flush_interval=5.0,
max_retries=3,
),
)
Configuration Management
Configuration from Dictionary
import asyncio
import aiologging
config = {
"version": 1,
"loggers": {
"myapp": {
"level": "INFO",
"handlers": ["console", "file"]
}
},
"handlers": {
"console": {
"class": "stream",
"level": "INFO",
"stream": "stdout"
},
"file": {
"class": "file",
"level": "DEBUG",
"filename": "app.log",
"mode": "a"
}
}
}
aiologging.configure_from_dict(config)
async def main():
logger = aiologging.get_configured_logger("myapp")
await logger.info("This uses configured logger")
await aiologging.shutdown()
asyncio.run(main())
Configuration from File
aiologging.configure_from_file("logging_config.json")
Migration from Standard Logging
# before
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("app")
logger.info("Message")
# after
import aiologging
aiologging.basicConfig(level=aiologging.INFO)
logger = aiologging.getLogger("app")
await logger.info("Message") # + await
await aiologging.shutdown() # once, at application exit
Key differences:
- Await the logging methods —
debug/info/.../critical,logandexceptionare coroutines; everything else (setLevel,addHandler,getChild, ...) stays synchronous. - Call
await aiologging.shutdown()at exit — the queue must be drained while the event loop is still running. - Handlers are async — use the
Async*handler classes (or bridge stdlib records withcaptureStdlib()).
API Reference
Logger Methods
Async (require await):
await logger.log(level, msg, *args, **kwargs)await logger.debug/info/warning/error/critical(msg, *args, **kwargs)await logger.exception(msg, *args, exc_info=True, **kwargs)
All accept the standard keyword arguments: exc_info, extra, stack_info, stacklevel.
Sync (identical to logging.Logger):
setLevel(level),getEffectiveLevel(),isEnabledFor(level)addHandler(h)/removeHandler(h)/hasHandlers()addFilter(f)/removeFilter(f)/filter(record)getChild(suffix)/getChildren()makeRecord(...),findCaller(stack_info, stacklevel)- attributes:
name,level,parent,propagate,handlers,filters,disabled
Module Functions
getLogger(name=None)— hierarchical loggers; no name returns the root loggerbasicConfig(level, format, datefmt, handlers, force, queue_size, overflow, delivery, capture_stdlib)await flush()— wait until every queued record has been handledawait shutdown()— drain the queue and close all handlersdisable(level)— likelogging.disablecaptureStdlib(capture=True, level=NOTSET)— bridge stdlib logging recordsawait debug/info/warning/error/exception/critical/log(...)— root-logger convenience coroutines
Handler Classes
AsyncHandler- Base async handlerAsyncStreamHandler- Stream output handlerAsyncFileHandler- File output handler (requires aiofiles)AsyncRotatingFileHandler- Size-based rotation (requires aiofiles)AsyncTimedRotatingFileHandler- Time-based rotation (requires aiofiles)AsyncHttpHandler- Universal HTTP handler (requires aiohttp)AsyncHttpTextHandler- Plain text HTTP handler (requires aiohttp)AsyncHttpJsonHandler- JSON HTTP handler (requires aiohttp)AsyncHttpProtoHandler- Protobuf HTTP handler (requires aiohttp, protobuf)StdlibBridgeHandler- Synclogging.Handlerforwarding records into aiologging
Configuration Classes
BatchConfig- Batch processing configurationFileConfig- File handler configurationHttpConfig- HTTP handler configurationLoggerConfig- Logger configurationRotationConfig- Rotation configuration for file handlers
Exception Classes
AiologgingError- Base exception for all aiologging errorsHandlerError- Base exception for handler errorsConfigurationError- Configuration-related errorsDependencyError- Missing optional dependenciesAuthenticationError- Authentication failuresNetworkError- Network-related errorsFileError- File operation errorsRotationError- File rotation errorsBatchError- Batch processing errorsFormatterError- Formatting errorsLoggerError- Logger operation errorsContextError- Context manager errors
Performance Considerations
- Delivery mode: the default
enqueuemakes logging nearly free for the caller; reservedelivery="await"for records that must be confirmed - Backpressure: pick the overflow policy consciously —
blocknever loses records,drop_*never stalls the application - Buffering: use buffered/batching handlers for high-volume logging
- Metrics Collection: enable metrics to monitor drops and errors
- Rate Limiting: use rate limiters to prevent log flooding
- Shutdown: always
await aiologging.shutdown()before exit so nothing queued is lost
Testing
# Install development dependencies
pip install aiologging[dev]
# Run tests
pytest
# Run tests with coverage
pytest --cov=aiologging
# Run type checking
mypy aiologging
Contributing
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass and type checking succeeds
- Submit a pull request
License
MIT License - see LICENSE file for details.
Changelog
0.2.0
Breaking: the logging pipeline is now queue-based and the API strictly follows the standard logging module.
- Records are enqueued at the call site and dispatched by a background consumer —
await logger.info(...)no longer waits for handler I/O - Configurable
delivery("enqueue"/"await") andoverflow("block"/"drop_new"/"drop_old") viabasicConfigor per logger await aiologging.flush()/await aiologging.shutdown()lifecycle; the consumer starts lazily and survives event loop changes- Stdlib bridge:
captureStdlib()/basicConfig(capture_stdlib=True)routes standardloggingrecords through async handlers - Module-level convenience coroutines (
aiologging.info(...), ...) andaiologging.disable(level) - API aligned with
logging.Logger:getEffectiveLevel,getChild,getChildren,hasHandlers,makeRecord,findCaller,stacklevelsupport,parent/propagate/filtersattributes;getLogger()without arguments returns the root logger - Removed:
Logger.setFormatter/getFormatter(formatters live on handlers),getLevel(usegetEffectiveLevel),disable()/enable()methods (use thedisabledattribute oraiologging.disable),setParent/getParent,getRootLogger,getLoggerContext,log_async async with logger:now flushes instead of closing the shared logger instance- Handlers can be constructed outside a running event loop on Python 3.9
0.1.1
- Python 3.13 and 3.14 support
- Replace deprecated
asyncio.iscoroutinefunctionwithinspect.iscoroutinefunction - Remove dead Python 3.8 compatibility branches
0.1.0
- Initial release
- Full async logging API
- Stream, file, and HTTP handlers
- File rotation support
- Extensible authentication
- Optional dependencies
- Strict type checking
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 aiologging-0.2.0.tar.gz.
File metadata
- Download URL: aiologging-0.2.0.tar.gz
- Upload date:
- Size: 83.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
36a8253a2ab02f60016ca8b4fa7a3bd578c98f043dca4fae7d734802f0d37d70
|
|
| MD5 |
e20b8766aa54c8f15dda303ee6151e28
|
|
| BLAKE2b-256 |
b279f9ac3f45c2a4d3271456238aa8799b31d51f216a48bf2e3ff7a1fb5f47fa
|
File details
Details for the file aiologging-0.2.0-py3-none-any.whl.
File metadata
- Download URL: aiologging-0.2.0-py3-none-any.whl
- Upload date:
- Size: 59.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
91b9c68b4093f55582fb95e98bee4359eeebfc9e4f6882af2b8bc72554509a2f
|
|
| MD5 |
9ec34e239e0ba17714a890d8e918d4c1
|
|
| BLAKE2b-256 |
b406d2a63f0b0496812b9fe604b7b6cd28f6cc5f4e35f4b0e12a9d2d14e3d1d0
|