# rotary_logger
<!--
-- +==== BEGIN rotary_logger =================+
-- LOGO:
-- ..........####...####..........
-- ......###.....#.#########......
-- ....##........#.###########....
-- ...#..........#.############...
-- ...#..........#.#####.######...
-- ..#.....##....#.###..#...####..
-- .#.....#.##...#.##..##########.
-- #.....##########....##...######
-- #.....#...##..#.##..####.######
-- .#...##....##.#.##..###..#####.
-- ..#.##......#.#.####...######..
-- ..#...........#.#############..
-- ..#...........#.#############..
-- ...##.........#.############...
-- ......#.......#.#########......
-- .......#......#.########.......
-- .........#####...#####.........
-- /STOP
-- PROJECT: rotary_logger
-- FILE: README.md
-- LAST Modified: 6:10:51 02-11-2025
-- LAST Modified: 5:3:14 02-11-2025
-- DESCRIPTION:
-- A module that provides a universal python light on iops way of logging to files your program execution.
-- /STOP
-- COPYRIGHT: (c) Asperguide
-- PURPOSE: This is the readme of the project, an explanation of the aim of the project as well as how to set it up or contribute.
-- // AR
-- +==== END rotary_logger =================+
-->

## Table of contents
- [Features](#features)
- [Installation](#installation)
- [Quickstart](#quickstart)
- [CLI](#cli)
- [Library usage](#library-usage)
- [Configuration & options](#configuration--options)
- [Running tests](#running-tests)
- [Development notes](#development-notes)
- [Documentation (Doxygen)](#documentation-doxygen)
- [Contributing](#contributing)
- [License](#license)
## Features
- Mirror stdout and stderr into rotating files
- Optionally merge stdout/stderr into a single file or keep them split into separate per-stream folders
- Low-IO buffered writes using a swap-buffer flush strategy
- Safe concurrent use (per-object RLocks; see developer notes on lock ordering)
## Installation
From PyPI (when published):
```bash
pip install rotary_logger
```
From source (recommended for development):
```bash
git clone https://github.com/Hanra-s-work/rotary_logger.git
cd rotary_logger
python -m venv .venv
source .venv/bin/activate
pip install -e .[dev]
```
If you only need the package without dev dependencies:
```bash
pip install -e .
```
## Quickstart
### CLI
You can use the package as a CLI similar to `tee`:
```bash
echo "hello" | python -m rotary_logger -a my_log
```
You can also use the shortcut: `pytee`.
It works similarly to `python -m rotary_logger`.
Because we are similare but not identical, the destination you can specify only corresponds to the base folder that will be used, the rest of the directory will be built in the following way: `path/to/folder/year/month/day/` then in the day, if you pass the `-m` (`--merge`) you will see files that are timestamped, however, if you do not pass the flag, you will see 2 folders, `stdout` and `stderr` which will each contain timestamped files.
### Library usage
Embed RotaryLogger in your application:
```python
from pathlib import Path
from rotary_logger import RotaryLogger, RL_CONST
_path_to_store_the_log: Path = Path('/var/log/myapp')
RL = RotaryLogger(
log_to_file=True,
override=False,
default_max_filesize=(2*RL_CONST.GB1),
merge_streams=False # Set to True if you want your stdout and stderr to be put into the same file instead of seperate.
)
RL.start_logging(log_folder=_path_to_store_the_log, merged=False)
```
This replaces `sys.stdout` and `sys.stderr` with `TeeStream` wrappers. If you need to stop logging in-process, restore `sys.stdout` and `sys.stderr` yourself. Or call the `stop_logging` function.
## Documentation
You can find documentation here: [docs](./docs_input/) as well as here: [https://hanra-s-work.github.io/rotary_logger/](https://hanra-s-work.github.io/rotary_logger/)
## Logo source
The source of the logo used in the documentation: [https://deepai.org](https://deepai.org), then edited in gimp.
## Configuration & options
### Common CLI options
- `-a` / `--append`: append to existing logs (do not truncate)
- `-m` / `--merge`: merge stdout and stderr into a single log file
- `-s` / `--max-size`: maximum file size in MB before rotation
### Library API (short)
- `RotaryLogger.start_logging(log_folder: Optional[Path], merged: bool, append: bool, max_size_mb: int)` — begin capturing and rotating logs
Refer to the module docs (or docstrings) for full API details.
## Running tests
The project uses pytest. From the repository root (inside your `virtualenv`):
```bash
pip install -r requirements.txt
pytest -q
```
To run the CI-like test harness used during development, use `action_test.sh` (requires Docker):
```bash
./action_test.sh
```
## Development notes
- Locking: the code uses per-object `threading.RLock` instances. The recommended pattern is to snapshot minimal state while holding the lock, release the lock to perform blocking I/O, then re-acquire to commit state. This avoids holding locks during filesystem operations.
- By default, logs are written under the `logs/` folder inside the package directory unless a `log_folder` is supplied.
## Control functions (library API)
RotaryLogger exposes a small set of control functions to manage in-process log capturing. These are safe to call from multiple threads, but there are a few rules and guarantees to understand:
- start_logging(log_folder: Optional[Path]=None, max_filesize: Optional[int]=None, merged: Optional[bool]=None, log_to_file: bool=True) -> None
- Start redirecting `sys.stdout` and `sys.stderr` to `TeeStream` wrappers and begin writing to rotating files.
- Parameters: `log_folder` — optional base folder to write logs; `max_filesize` — override rotation size; `merged` — whether to merge stdout/stderr into a single file; `log_to_file` — whether to enable file writes.
- Thread-safety: the function snapshots configuration under an internal lock and performs filesystem checks outside the lock; assignment of `sys.stdout`/`sys.stderr` is performed atomically while holding the lock.
- stop_logging() -> None
- Stop capturing and restore the original `sys.stdout`/`sys.stderr` objects.
- This function flushes buffers and also attempts to unregister any atexit flush handlers that were registered by `start_logging`.
- Thread-safety: restores streams while holding the internal lock and flushes outside the lock to avoid blocking critical sections.
- pause_logging() -> bool
- Toggle the pause state. When pausing, the TeeStreams are uninstalled and the original streams are restored; when resuming, the TeeStreams are reinstalled.
- Returns the new paused state (True when paused).
- Thread-safety: updates and stream replacements are done while holding the internal lock; expensive flushes are executed outside the lock.
- resume_logging() -> bool
- Explicitly resume logging (idempotent). Returns the paused state after resuming (False).
- Thread-safety: same guarantees as `pause_logging`.
- is_logging() -> bool
- Returns True when logging is active (a TeeStream is installed and the logger is not paused).
- Safe to call concurrently.
- is_redirected(stream: StdMode) -> bool
- Query whether the given stream (StdMode.STDOUT, STDIN, STDERR) is currently redirected to a TeeStream.
### Notes
- **atexit handlers**: RotaryLogger registers flush handlers via `atexit.register` to attempt a final flush at process exit; those handlers are unregistered when `stop_logging()` is called. The implementation stores the exact bound-methods used to guarantee `atexit.unregister` works reliably.
- **Concurrency testing**: basic concurrent toggling of pause/resume is covered by the project's tests. Calling `start_logging`/`stop_logging` concurrently from multiple threads is heavier and may involve filesystem operations — avoid such patterns in production unless you synchronize externally.
## Documentation (Doxygen)
The project uses generated Doxygen in different formats:
- [`HTML`](https://hanra-s-work.github.io/rotary_logger/)
- `LaTeX` (every time a version is released)
- `RTF` (every time a version is released)
You can view the documentation online, by going here: [https://hanra-s-work.github.io/rotary_logger/](https://hanra-s-work.github.io/rotary_logger/)
## Contributing
Please read [`CONTRIBUTING.md`](./CONTRIBUTING.md) and [`CODE_OF_CONDUCT.md`](./CODE_OF_CONDUCT.md) before opening issues or PRs.
## License
See the [`LICENSE`](./LICENSE) file in the repository.
Raw data
{
"_id": null,
"home_page": null,
"name": "rotary-logger",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.9",
"maintainer_email": "Henry Letellier <henrysoftwarehouse@protonmail.com>",
"keywords": "logger, rotary, server, lightweight, iops, stdout, stderr, optimised, logging, pure python, no deps",
"author": null,
"author_email": "Henry Letellier <henrysoftwarehouse@protonmail.com>",
"download_url": "https://files.pythonhosted.org/packages/65/bc/d1b50970bf9ec7d6d78d2e510a85410a95af1e9693f7bbf1a400708c80fc/rotary_logger-1.0.0.tar.gz",
"platform": null,
"description": "# rotary_logger\n<!-- \n-- +==== BEGIN rotary_logger =================+\n-- LOGO: \n-- ..........####...####..........\n-- ......###.....#.#########......\n-- ....##........#.###########....\n-- ...#..........#.############...\n-- ...#..........#.#####.######...\n-- ..#.....##....#.###..#...####..\n-- .#.....#.##...#.##..##########.\n-- #.....##########....##...######\n-- #.....#...##..#.##..####.######\n-- .#...##....##.#.##..###..#####.\n-- ..#.##......#.#.####...######..\n-- ..#...........#.#############..\n-- ..#...........#.#############..\n-- ...##.........#.############...\n-- ......#.......#.#########......\n-- .......#......#.########.......\n-- .........#####...#####.........\n-- /STOP\n-- PROJECT: rotary_logger\n-- FILE: README.md\n-- LAST Modified: 6:10:51 02-11-2025\n-- LAST Modified: 5:3:14 02-11-2025\n-- DESCRIPTION: \n-- A module that provides a universal python light on iops way of logging to files your program execution.\n-- /STOP\n-- COPYRIGHT: (c) Asperguide\n-- PURPOSE: This is the readme of the project, an explanation of the aim of the project as well as how to set it up or contribute.\n-- // AR\n-- +==== END rotary_logger =================+\n-->\n\n\n## Table of contents\n\n- [Features](#features)\n- [Installation](#installation)\n- [Quickstart](#quickstart)\n - [CLI](#cli)\n - [Library usage](#library-usage)\n- [Configuration & options](#configuration--options)\n- [Running tests](#running-tests)\n- [Development notes](#development-notes)\n- [Documentation (Doxygen)](#documentation-doxygen)\n- [Contributing](#contributing)\n- [License](#license)\n\n## Features\n\n- Mirror stdout and stderr into rotating files\n- Optionally merge stdout/stderr into a single file or keep them split into separate per-stream folders\n- Low-IO buffered writes using a swap-buffer flush strategy\n- Safe concurrent use (per-object RLocks; see developer notes on lock ordering)\n\n## Installation\n\nFrom PyPI (when published):\n\n```bash\npip install rotary_logger\n```\n\nFrom source (recommended for development):\n\n```bash\ngit clone https://github.com/Hanra-s-work/rotary_logger.git\ncd rotary_logger\npython -m venv .venv\nsource .venv/bin/activate\npip install -e .[dev]\n```\n\nIf you only need the package without dev dependencies:\n\n```bash\npip install -e .\n```\n\n## Quickstart\n\n### CLI\n\nYou can use the package as a CLI similar to `tee`:\n\n```bash\necho \"hello\" | python -m rotary_logger -a my_log\n```\n\nYou can also use the shortcut: `pytee`.\n\nIt works similarly to `python -m rotary_logger`.\n\nBecause we are similare but not identical, the destination you can specify only corresponds to the base folder that will be used, the rest of the directory will be built in the following way: `path/to/folder/year/month/day/` then in the day, if you pass the `-m` (`--merge`) you will see files that are timestamped, however, if you do not pass the flag, you will see 2 folders, `stdout` and `stderr` which will each contain timestamped files.\n\n### Library usage\n\nEmbed RotaryLogger in your application:\n\n```python\nfrom pathlib import Path\nfrom rotary_logger import RotaryLogger, RL_CONST\n\n_path_to_store_the_log: Path = Path('/var/log/myapp')\n\nRL = RotaryLogger(\n log_to_file=True,\n override=False,\n default_max_filesize=(2*RL_CONST.GB1),\n merge_streams=False # Set to True if you want your stdout and stderr to be put into the same file instead of seperate.\n)\nRL.start_logging(log_folder=_path_to_store_the_log, merged=False)\n```\n\nThis replaces `sys.stdout` and `sys.stderr` with `TeeStream` wrappers. If you need to stop logging in-process, restore `sys.stdout` and `sys.stderr` yourself. Or call the `stop_logging` function.\n\n## Documentation\n\nYou can find documentation here: [docs](./docs_input/) as well as here: [https://hanra-s-work.github.io/rotary_logger/](https://hanra-s-work.github.io/rotary_logger/)\n\n## Logo source\n\nThe source of the logo used in the documentation: [https://deepai.org](https://deepai.org), then edited in gimp.\n\n## Configuration & options\n\n### Common CLI options\n\n- `-a` / `--append`: append to existing logs (do not truncate)\n- `-m` / `--merge`: merge stdout and stderr into a single log file\n- `-s` / `--max-size`: maximum file size in MB before rotation\n\n### Library API (short)\n\n- `RotaryLogger.start_logging(log_folder: Optional[Path], merged: bool, append: bool, max_size_mb: int)` \u2014 begin capturing and rotating logs\n\nRefer to the module docs (or docstrings) for full API details.\n\n## Running tests\n\nThe project uses pytest. From the repository root (inside your `virtualenv`):\n\n```bash\npip install -r requirements.txt\npytest -q\n```\n\nTo run the CI-like test harness used during development, use `action_test.sh` (requires Docker):\n\n```bash\n./action_test.sh\n```\n\n## Development notes\n\n- Locking: the code uses per-object `threading.RLock` instances. The recommended pattern is to snapshot minimal state while holding the lock, release the lock to perform blocking I/O, then re-acquire to commit state. This avoids holding locks during filesystem operations.\n- By default, logs are written under the `logs/` folder inside the package directory unless a `log_folder` is supplied.\n\n## Control functions (library API)\n\nRotaryLogger exposes a small set of control functions to manage in-process log capturing. These are safe to call from multiple threads, but there are a few rules and guarantees to understand:\n\n- start_logging(log_folder: Optional[Path]=None, max_filesize: Optional[int]=None, merged: Optional[bool]=None, log_to_file: bool=True) -> None\n - Start redirecting `sys.stdout` and `sys.stderr` to `TeeStream` wrappers and begin writing to rotating files.\n - Parameters: `log_folder` \u2014 optional base folder to write logs; `max_filesize` \u2014 override rotation size; `merged` \u2014 whether to merge stdout/stderr into a single file; `log_to_file` \u2014 whether to enable file writes.\n - Thread-safety: the function snapshots configuration under an internal lock and performs filesystem checks outside the lock; assignment of `sys.stdout`/`sys.stderr` is performed atomically while holding the lock.\n\n- stop_logging() -> None\n - Stop capturing and restore the original `sys.stdout`/`sys.stderr` objects.\n - This function flushes buffers and also attempts to unregister any atexit flush handlers that were registered by `start_logging`.\n - Thread-safety: restores streams while holding the internal lock and flushes outside the lock to avoid blocking critical sections.\n\n- pause_logging() -> bool\n - Toggle the pause state. When pausing, the TeeStreams are uninstalled and the original streams are restored; when resuming, the TeeStreams are reinstalled.\n - Returns the new paused state (True when paused).\n - Thread-safety: updates and stream replacements are done while holding the internal lock; expensive flushes are executed outside the lock.\n\n- resume_logging() -> bool\n - Explicitly resume logging (idempotent). Returns the paused state after resuming (False).\n - Thread-safety: same guarantees as `pause_logging`.\n\n- is_logging() -> bool\n - Returns True when logging is active (a TeeStream is installed and the logger is not paused).\n - Safe to call concurrently.\n\n- is_redirected(stream: StdMode) -> bool\n - Query whether the given stream (StdMode.STDOUT, STDIN, STDERR) is currently redirected to a TeeStream.\n\n### Notes\n\n- **atexit handlers**: RotaryLogger registers flush handlers via `atexit.register` to attempt a final flush at process exit; those handlers are unregistered when `stop_logging()` is called. The implementation stores the exact bound-methods used to guarantee `atexit.unregister` works reliably.\n- **Concurrency testing**: basic concurrent toggling of pause/resume is covered by the project's tests. Calling `start_logging`/`stop_logging` concurrently from multiple threads is heavier and may involve filesystem operations \u2014 avoid such patterns in production unless you synchronize externally.\n\n## Documentation (Doxygen)\n\nThe project uses generated Doxygen in different formats:\n\n- [`HTML`](https://hanra-s-work.github.io/rotary_logger/)\n- `LaTeX` (every time a version is released)\n- `RTF` (every time a version is released)\n\nYou can view the documentation online, by going here: [https://hanra-s-work.github.io/rotary_logger/](https://hanra-s-work.github.io/rotary_logger/)\n\n## Contributing\n\nPlease read [`CONTRIBUTING.md`](./CONTRIBUTING.md) and [`CODE_OF_CONDUCT.md`](./CODE_OF_CONDUCT.md) before opening issues or PRs.\n\n## License\n\nSee the [`LICENSE`](./LICENSE) file in the repository.\n",
"bugtrack_url": null,
"license": null,
"summary": "A module that provides a universal python light on iops way of logging to files your program execution.",
"version": "1.0.0",
"project_urls": {
"Documentation": "https://github.com/Hanra-s-work/rotary_logger/blob/main/README.md",
"Homepage": "https://github.com/Hanra-s-work/rotary_logger",
"Issues": "https://github.com/Hanra-s-work/rotary_logger/issues",
"Repository": "https://github.com/Hanra-s-work/rotary_logger.git"
},
"split_keywords": [
"logger",
" rotary",
" server",
" lightweight",
" iops",
" stdout",
" stderr",
" optimised",
" logging",
" pure python",
" no deps"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "f5dc1bf59784519d862cc4f67c0627b08a286ac0b295b6aa25ca08fa201a68b7",
"md5": "29eede54c2bc6007206afb212e714f38",
"sha256": "fa22e72168d97d027f4a5c922c1b3a883ed4ae67b329fd51bcdc6da29b48ffb8"
},
"downloads": -1,
"filename": "rotary_logger-1.0.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "29eede54c2bc6007206afb212e714f38",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.9",
"size": 28062,
"upload_time": "2025-11-02T05:17:37",
"upload_time_iso_8601": "2025-11-02T05:17:37.446221Z",
"url": "https://files.pythonhosted.org/packages/f5/dc/1bf59784519d862cc4f67c0627b08a286ac0b295b6aa25ca08fa201a68b7/rotary_logger-1.0.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "65bcd1b50970bf9ec7d6d78d2e510a85410a95af1e9693f7bbf1a400708c80fc",
"md5": "56605f49437cc536a2420009e52643a6",
"sha256": "edf949cd7b8527dca6ae37140bc84f33e4aee5888306882f9c995bf8cb5f47b2"
},
"downloads": -1,
"filename": "rotary_logger-1.0.0.tar.gz",
"has_sig": false,
"md5_digest": "56605f49437cc536a2420009e52643a6",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.9",
"size": 31402,
"upload_time": "2025-11-02T05:17:38",
"upload_time_iso_8601": "2025-11-02T05:17:38.646171Z",
"url": "https://files.pythonhosted.org/packages/65/bc/d1b50970bf9ec7d6d78d2e510a85410a95af1e9693f7bbf1a400708c80fc/rotary_logger-1.0.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-11-02 05:17:38",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "Hanra-s-work",
"github_project": "rotary_logger",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"requirements": [],
"lcname": "rotary-logger"
}