concurrent-log-handler
RotatingFileHandler replacement with concurrency, gzip and Windows support. Size and time based rotation.
Description
Concurrent Log Handler (CLH)
The concurrent-log-handler package provides robust logging handlers for Python's standard logging package (PEP 282).
It enables multiple processes (and threads) to safely write to a single log file, with built-in support for size-based
and time-based log rotation and optional log compression.
This package is meant for applications that run in multiple processes, potentially across different hosts sharing a network drive, and require a centralized logging solution without the complexity of external logging services.
NOTE: If you're reading this and the links to files like CHANGELOG.md don't work, or links within the document either, try viewing this document on GitHub instead.
What's new
See CHANGELOG.md for details.
-
Version 0.9.29: (February 2026)
- Fix race conditions when a handler created before
fork()is used by multiple child processes. Child processes that inherit a handler now automatically reopen the lock file for independent lock isolation, and an in-process threading lock prevents concurrent threads from bypassing flock() serialization.- Re-initializing logging post-fork (see Usage Guidelines) is still recommended.
- Depend on portalocker >= 2.6.0 instead of 1.6.0. Earlier versions of portalocker on Windows can be problematic.
- Add
finalize_handler_configuration()hook to ConcurrentTimedRotatingFileHandler to allow customization of the handler before the first rollover.
- Fix race conditions when a handler created before
-
Version 0.9.28: (June 10th, 2025)
-
Fixes errors when apps, esp. asyncio based, try to log during interpreter shutdown. Issue #80
-
Fix missing rollovers when a worker was restarted before the next logging event, but after the last scheduled rollover. Issue #81
-
Version 0.9.27: (June 6th, 2025)
-
Important Notice (June 2025): Background Logging Utility Removed
- The
concurrent_log_handler.queuemodule was removed in version 0.9.28. - It has compatibility issues with complex logging setups and other robustness concerns.
- Recommendation:
- Simply use the standard CLH handlers (
ConcurrentRotatingFileHandlerorConcurrentTimedRotatingFileHandler) directly in your application. Synchronous logging calls are simpler, more reliable, and performant enough for most use cases. - If you need non-blocking logging calls, use the standard library patterns shown in Performance Patterns.
- Simply use the standard CLH handlers (
- The core CLH handlers remain fully supported and are not affected by this deprecation.
- The
-
Version 0.9.26: (May 2025)
- Improved performance, especially on POSIX systems.
- Added testing for Python 3.13 and improved project configuration and documentation.
Key Features
- Concurrent Logging: Multiple processes and threads safely write to the same log file.
- File Rotation: Size-based and time-based rotation with optional compression.
- Cross-Platform: Windows and POSIX support with reliable file locking.
- Customizable: Control naming, permissions, line endings, and lock file placement.
- Performance Optimized: Keeps files open between writes for better performance.
- Python 3.6 through current versions: Modern Python support.
- Focused Design: Reliably handles file operations. For non-blocking behavior, see our recommended Application-Level Performance Patterns, including patterns for graceful degradation to synchronous logging, or using sync logging only for higher priority levels.
Primary Use Cases
CLH is primarily designed for scenarios where:
- Multiple processes of a Python application need to log to a shared file.
- These processes might be on the same machine or on different machines accessing a shared network drive.
- Log files need to be automatically rotated based on size or time.
Note that this package is not primarily intended for intensive high-throughput logging scenarios, but rather for general-purpose logging in multi-process applications.
Alternatives to CLH
While CLH offers a robust file-based solution, consider these alternatives for different needs:
- Cloud Logging Services: Azure Monitor, AWS CloudWatch Logs, Google Cloud Logging, Logstash, etc. These are excellent for distributed systems and offer advanced analysis features.
- Custom Logging Server: Implement a centralized logging server as demonstrated in
the Python Logging Cookbook.
CLH's
QueueHandlerandQueueListenercan be adapted for this pattern.
Installation
Install the package using pip:
pip install concurrent-log-handler
This will also install portalocker. On Windows, portalocker has a dependency on pywin32.
Note that we now require at least portalocker 2.6.0, which among other things, explicitly
declares its dependency on pywin32 if needed.
To install from source:
python setup.py install
Quick Start: Basic Usage
Here's a simple example using ConcurrentRotatingFileHandler for size-based rotation:
import logging
from concurrent_log_handler import ConcurrentRotatingFileHandler
import os
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
# Use an absolute path for the log file
logfile = os.path.abspath("mylogfile.log")
# Configure the handler: rotate after 512KB, keep 5 backups
# Mode "a" for append
rotate_handler = ConcurrentRotatingFileHandler(
logfile, "a", maxBytes=512 * 1024, backupCount=5
)
logger.addHandler(rotate_handler)
logger.info("This is an exciting log message!")
logger.info("Multiple processes can write here concurrently.")
For a few more basic code examples, see src/example.py. For more advanced usage including non-blocking patterns, see the Performance Patterns guide.
Important Usage Guidelines
To ensure correct and reliable operation when using CLH in a multi-process environment, please keep the following in mind:
-
Handler Instantiation per Process:
- Each process must create its own instance of the CLH handler (
ConcurrentRotatingFileHandlerorConcurrentTimedRotatingFileHandler). - You cannot serialize a handler instance and reuse it in another process.
- For example, you cannot pass it from a parent to a child process via
multiprocessingin spawn mode.
- For example, you cannot pass it from a parent to a child process via
- This limitation is because the file lock objects and other internal states within the handler cannot be safely serialized and shared across process boundaries.
- This requirement does not apply to threads within the same process; threads can share a single CLH instance.
- This requirement also applies to child processes created via
fork()(e.g., Gunicorn with--preload, uWSGI, ormultiprocessingwith the fork start method). Forked children inherit the parent's file descriptors, which causes file locks to silently fail to serialize between processes. See item 3 below for the recommended pattern.
- Each process must create its own instance of the CLH handler (
-
Multiprocessing and Spawn mode:
Just to reemphasize the point above:
- If you use
multiprocessingwith the defaultspawnstart method (the default on Windows and macOS), each child process must create its own CLH handler instance. - In your child process startup code, instantiate the handler as shown in the example above.
- Usually this means you can call your standard logging setup function in the child.
- Don't just initialize your logging code in the parent process and allow child processes to inherit loggers.
- If you use
-
Using
fork()(Gunicorn, uWSGI, multiprocessing withforkstart method):
On Linux, depending on the version of Python, multiprocessing defaults to the fork start method. Frameworks like
Gunicorn with --preload and uWSGI also fork worker processes from a parent that has already initialized logging.
While CLH v0.9.29+ has in