Stateful incremental reading of rotated log files with persisted inode/seek position.
Project description
tailstate
Rotation-aware incremental reading of rotated log files with persisted state.
The importable package is tailstate, with code under src/tailstate/. The
distribution name in pyproject.toml is tailstate.
It persists inode + byte offset as JSON so a later run can continue from the
last fully processed line, or follow rotation to unread files with the same
basename prefix.
- Python: 3.11+
- Runtime dependencies: standard library only
- State format: JSON (UTF-8 text), human-readable and stable across Python versions
- License: MIT (see LICENSE)
Docs
README.md: overview, install, quickstart- docs/development.md: dev commands, tests, layout, portability notes
AGENTS.md: non-obvious traps for coding agentssrc/tailstate/: module and class docstrings define the precise behavior
Main Pieces
RotatedLogFileSavedState: discovers matching log files in one directory, ordered by mtime, and yields open text streams fromlogs()LogSavedState: typed persistedinode/seekstateTimedLogProcessor: processes yielded files under a totalSIGALRMbudgetPersistentObjandJsonPersistentObj: generic JSON-backed persistence helpersLog4jLogLineProcessor: timed processor for log4j-style%d{ISO8601} %p %m%nlinesensure_dir,tmp_file,find_file_by_inode: filesystem helpers
Important Behavior
- Matching uses a plain filename prefix in the same directory.
app.log.1andapp.log-extraboth matchapp.log. logs()yields streams from inside an open-file context. The file stays open until the caller resumes the generator.- If byte offsets matter, use
readline()or explicittell()/seek()instead offor line in f. - Log streams are opened as UTF-8 with
errors="replace". - Timed processing uses
SIGALRMon Unix. Timeouts can still affect rotation state and partial progress in subtle ways.
Install
Using uv from the project root:
uv sync
uv run pytest
uv run mypy src tests
uv run black src tests
Without uv:
python3 -m venv .venv && source .venv/bin/activate
pip install -e .
pip install black mypy pytest pytest-cov
python -m pytest
Demo
python3 -m tailstate.example_standalone
Quickstart
Incremental rotation-aware reading:
from tailstate import RotatedLogFileSavedState
with RotatedLogFileSavedState("/var/log/app/app.log", "/var/run/app-state.json") as state:
for log_file in state.logs():
while True:
line = log_file.readline()
if not line:
break
...
Timed multi-file processing:
from tailstate import RotatedLogFileSavedState, TimedLogProcessor
class MyProc(TimedLogProcessor):
def process_log(self, log_file):
... # return (value, skip_others)
def combine_values(self, old, new):
...
with RotatedLogFileSavedState(log_path, state_path) as state:
result = MyProc(max_duration=60).process(state)
log4j-style line parsing:
from tailstate import Log4jLogLineProcessor
class MyScrape(Log4jLogLineProcessor):
def get_metrics(self):
return {"level": {"error": 0}}
def process_level_error(self, message):
return {"level": {"error": 1}}
License
MIT. See LICENSE.
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 tailstate-0.1.0.tar.gz.
File metadata
- Download URL: tailstate-0.1.0.tar.gz
- Upload date:
- Size: 53.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
da477dcd1ceeb1c6cafccc7ab66a5ed436580626f160a8e72e65d0c400f11015
|
|
| MD5 |
990f8a977424861694b6df765120e658
|
|
| BLAKE2b-256 |
289084019a164ea65522d11b981b32035f3b5e4daf6de8452939dd125b4ee9f2
|
Provenance
The following attestation bundles were made for tailstate-0.1.0.tar.gz:
Publisher:
publish.yml on dajobe/tailstate
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tailstate-0.1.0.tar.gz -
Subject digest:
da477dcd1ceeb1c6cafccc7ab66a5ed436580626f160a8e72e65d0c400f11015 - Sigstore transparency entry: 1341117851
- Sigstore integration time:
-
Permalink:
dajobe/tailstate@40b51149aa61c62fde1121b7350646253e86b3dc -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/dajobe
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@40b51149aa61c62fde1121b7350646253e86b3dc -
Trigger Event:
push
-
Statement type:
File details
Details for the file tailstate-0.1.0-py3-none-any.whl.
File metadata
- Download URL: tailstate-0.1.0-py3-none-any.whl
- Upload date:
- Size: 13.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
53fe81519cd092209d1388f9441e06ea6461459008e5a5badfc9029cb42e03ff
|
|
| MD5 |
c2ad4c543508691c169fe70558f49233
|
|
| BLAKE2b-256 |
a7a6253c9d364250f9dc98a25c8d90b9136c68b091e15fe3e4542b816f00903e
|
Provenance
The following attestation bundles were made for tailstate-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on dajobe/tailstate
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tailstate-0.1.0-py3-none-any.whl -
Subject digest:
53fe81519cd092209d1388f9441e06ea6461459008e5a5badfc9029cb42e03ff - Sigstore transparency entry: 1341117933
- Sigstore integration time:
-
Permalink:
dajobe/tailstate@40b51149aa61c62fde1121b7350646253e86b3dc -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/dajobe
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@40b51149aa61c62fde1121b7350646253e86b3dc -
Trigger Event:
push
-
Statement type: