Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion hermes_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,24 @@
import os
import sys
import threading
from logging.handlers import RotatingFileHandler
from pathlib import Path
from typing import Optional, Sequence

# On Windows, stdlib ``RotatingFileHandler`` calls ``os.rename()`` in
# ``doRollover()`` and fails with ``PermissionError [WinError 32]`` whenever
# another process holds an append-mode handle on ``agent.log`` — which is
# essentially always in Hermes (TUI, gateway, ``hy_memory`` server, MCP
# servers, and on-demand CLI commands all log from separate processes),
# pinning ``agent.log`` at the 5 MiB threshold and spamming stderr with
# a traceback on every emit. ``concurrent-log-handler`` wraps the
# rename in a cross-process file lock (msvcrt on Windows, fcntl on POSIX)
# so only one process rotates at a time and the others wait their turn.
# Drop-in API compatibility — aliasing lets every existing
# ``RotatingFileHandler`` reference in this module (the class declaration,
# the ``isinstance`` checks, the docstring) keep working unchanged.
# See #44873.
from concurrent_log_handler import ConcurrentRotatingFileHandler as RotatingFileHandler # noqa: E402

from hermes_constants import get_config_path, get_hermes_home

# Sentinel to track whether setup_logging() has already run. The function
Expand Down
13 changes: 13 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,19 @@ dependencies = [
# install rather than gating it behind an extra + a mid-session lazy install
# (which deadlocked the CLI under prompt_toolkit — see #40490).
"Pillow==12.2.0",
# Windows log rotation. Stdlib ``RotatingFileHandler.doRollover()`` uses
# ``os.rename()`` which fails with ``PermissionError [WinError 32]`` on
# Windows whenever any other process holds an append-mode handle on
# ``agent.log`` (always the case in Hermes — TUI, gateway, ``hy_memory``
# server, MCP servers, and on-demand CLI commands all log from separate
# processes), pinning ``agent.log`` at the 5 MiB threshold and spamming
# stderr on every emit (see #44873). ``concurrent-log-handler`` wraps
# the rename in a cross-process file lock (msvcrt on Windows, fcntl on
# POSIX) so only one process rotates at a time and the others wait
# their turn. Aliasing the import in ``hermes_logging.py`` makes the
# swap transparent to every existing ``isinstance`` / class-declaration
# check. Pure-Python, no compiled deps.
"concurrent-log-handler==0.9.29",
]

[project.optional-dependencies]
Expand Down
7 changes: 5 additions & 2 deletions tests/test_hermes_logging.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
"""Tests for hermes_logging — centralized logging setup."""

import io
import logging
import os
import stat
import sys
import threading
from logging.handlers import RotatingFileHandler
# Same alias as hermes_logging.py so the autouse fixture's isinstance
# checks (which strip rotating handlers between tests) match the new
# CLH-backed handler class on installs that pulled in #44873's fix.
from concurrent_log_handler import ConcurrentRotatingFileHandler as RotatingFileHandler
from pathlib import Path
from unittest.mock import patch

Expand Down
26 changes: 26 additions & 0 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading