diff options
Diffstat (limited to 'pylogsentinel')
| -rw-r--r-- | pylogsentinel/core.py | 25 |
1 files changed, 16 insertions, 9 deletions
diff --git a/pylogsentinel/core.py b/pylogsentinel/core.py index 5ee033e..29ac0e4 100644 --- a/pylogsentinel/core.py +++ b/pylogsentinel/core.py @@ -351,11 +351,15 @@ class LockFile: class StateManager: """ - Manage per-inode state files. + Manage per-file state files keyed by device and inode. Format per file: <byte_offset> <line_number> + The state file is stored at "<state_dir>/<device>/<inode>" to uniquely + identify files across different filesystems (which may have overlapping + inode numbers). + If file is truncated (size < stored offset) we reset to 0,0. """ @@ -364,11 +368,11 @@ class StateManager: def __init__(self, state_dir: str) -> None: self.state_dir = state_dir - def _path_for_inode(self, inode: int) -> str: - return os.path.join(self.state_dir, str(inode)) + def _path_for_file(self, device: int, inode: int) -> str: + return os.path.join(self.state_dir, str(device), str(inode)) - def load(self, inode: int) -> tuple[int, int]: - path = self._path_for_inode(inode) + def load(self, device: int, inode: int) -> tuple[int, int]: + path = self._path_for_file(device, inode) try: with open(path, "r", encoding="utf-8") as fh: content = fh.read().strip() @@ -393,11 +397,13 @@ class StateManager: return 0, 0 return byte_offset, line_number - def save(self, inode: int, byte_offset: int, line_number: int) -> None: - path = self._path_for_inode(inode) + def save(self, device: int, inode: int, byte_offset: int, line_number: int) -> None: + path = self._path_for_file(device, inode) + device_dir = os.path.dirname(path) tmp_path = f"{path}.tmp" data = f"{byte_offset} {line_number}\n" try: + os.makedirs(device_dir, exist_ok=True) with open(tmp_path, "w", encoding="utf-8") as fh: fh.write(data) os.replace(tmp_path, path) @@ -711,8 +717,9 @@ class Sentinel: print(f"WARNING: Cannot stat {file_path}: {e}", file=sys.stderr) return + device = st.st_dev inode = st.st_ino - prev_offset, prev_line_no = self.state_manager.load(inode) + prev_offset, prev_line_no = self.state_manager.load(device, inode) lines, new_offset, new_last_line_no = read_new_lines( file_path, prev_offset, prev_line_no, self.max_block_size @@ -726,7 +733,7 @@ class Sentinel: action = self.actions.get(event.rule.action_id) or self.actions["default"] execute_action(action, event, skip_actions=self.skip_actions) - self.state_manager.save(inode, new_offset, new_last_line_no) + self.state_manager.save(device, inode, new_offset, new_last_line_no) def run(self) -> int: """ |
