Merge pull request #1251 from NousResearch/hermes/hermes-f7e92273
fix: prevent logging handler accumulation in gateway mode
This commit is contained in:
33
run_agent.py
33
run_agent.py
@@ -407,19 +407,30 @@ class AIAgent:
|
|||||||
|
|
||||||
# Persistent error log -- always writes WARNING+ to ~/.hermes/logs/errors.log
|
# Persistent error log -- always writes WARNING+ to ~/.hermes/logs/errors.log
|
||||||
# so tool failures, API errors, etc. are inspectable after the fact.
|
# so tool failures, API errors, etc. are inspectable after the fact.
|
||||||
from agent.redact import RedactingFormatter
|
# In gateway mode, each incoming message creates a new AIAgent instance,
|
||||||
_error_log_dir = _hermes_home / "logs"
|
# while the root logger is process-global. Re-adding the same errors.log
|
||||||
_error_log_dir.mkdir(parents=True, exist_ok=True)
|
# handler would cause each warning/error line to be written multiple times.
|
||||||
_error_log_path = _error_log_dir / "errors.log"
|
|
||||||
from logging.handlers import RotatingFileHandler
|
from logging.handlers import RotatingFileHandler
|
||||||
_error_file_handler = RotatingFileHandler(
|
root_logger = logging.getLogger()
|
||||||
_error_log_path, maxBytes=2 * 1024 * 1024, backupCount=2,
|
error_log_dir = _hermes_home / "logs"
|
||||||
|
error_log_path = error_log_dir / "errors.log"
|
||||||
|
resolved_error_log_path = error_log_path.resolve()
|
||||||
|
has_errors_log_handler = any(
|
||||||
|
isinstance(handler, RotatingFileHandler)
|
||||||
|
and Path(getattr(handler, "baseFilename", "")).resolve() == resolved_error_log_path
|
||||||
|
for handler in root_logger.handlers
|
||||||
)
|
)
|
||||||
_error_file_handler.setLevel(logging.WARNING)
|
if not has_errors_log_handler:
|
||||||
_error_file_handler.setFormatter(RedactingFormatter(
|
from agent.redact import RedactingFormatter
|
||||||
'%(asctime)s %(levelname)s %(name)s: %(message)s',
|
error_log_dir.mkdir(parents=True, exist_ok=True)
|
||||||
))
|
error_file_handler = RotatingFileHandler(
|
||||||
logging.getLogger().addHandler(_error_file_handler)
|
error_log_path, maxBytes=2 * 1024 * 1024, backupCount=2,
|
||||||
|
)
|
||||||
|
error_file_handler.setLevel(logging.WARNING)
|
||||||
|
error_file_handler.setFormatter(RedactingFormatter(
|
||||||
|
'%(asctime)s %(levelname)s %(name)s: %(message)s',
|
||||||
|
))
|
||||||
|
root_logger.addHandler(error_file_handler)
|
||||||
|
|
||||||
if self.verbose_logging:
|
if self.verbose_logging:
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
|
|||||||
@@ -6,13 +6,17 @@ are made.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
import logging
|
||||||
import re
|
import re
|
||||||
import uuid
|
import uuid
|
||||||
|
from logging.handlers import RotatingFileHandler
|
||||||
|
from pathlib import Path
|
||||||
from types import SimpleNamespace
|
from types import SimpleNamespace
|
||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
import run_agent
|
||||||
from honcho_integration.client import HonchoClientConfig
|
from honcho_integration.client import HonchoClientConfig
|
||||||
from run_agent import AIAgent, _inject_honcho_turn_context
|
from run_agent import AIAgent, _inject_honcho_turn_context
|
||||||
from agent.prompt_builder import DEFAULT_AGENT_IDENTITY
|
from agent.prompt_builder import DEFAULT_AGENT_IDENTITY
|
||||||
@@ -70,7 +74,7 @@ def agent_with_memory_tool():
|
|||||||
patch("run_agent.OpenAI"),
|
patch("run_agent.OpenAI"),
|
||||||
):
|
):
|
||||||
a = AIAgent(
|
a = AIAgent(
|
||||||
api_key="test-key-1234567890",
|
api_key="test-k...7890",
|
||||||
quiet_mode=True,
|
quiet_mode=True,
|
||||||
skip_context_files=True,
|
skip_context_files=True,
|
||||||
skip_memory=True,
|
skip_memory=True,
|
||||||
@@ -79,6 +83,60 @@ def agent_with_memory_tool():
|
|||||||
return a
|
return a
|
||||||
|
|
||||||
|
|
||||||
|
def test_aiagent_reuses_existing_errors_log_handler():
|
||||||
|
"""Repeated AIAgent init should not accumulate duplicate errors.log handlers."""
|
||||||
|
root_logger = logging.getLogger()
|
||||||
|
original_handlers = list(root_logger.handlers)
|
||||||
|
error_log_path = (run_agent._hermes_home / "logs" / "errors.log").resolve()
|
||||||
|
|
||||||
|
try:
|
||||||
|
for handler in list(root_logger.handlers):
|
||||||
|
root_logger.removeHandler(handler)
|
||||||
|
|
||||||
|
error_log_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
preexisting_handler = RotatingFileHandler(
|
||||||
|
error_log_path,
|
||||||
|
maxBytes=2 * 1024 * 1024,
|
||||||
|
backupCount=2,
|
||||||
|
)
|
||||||
|
root_logger.addHandler(preexisting_handler)
|
||||||
|
|
||||||
|
with (
|
||||||
|
patch(
|
||||||
|
"run_agent.get_tool_definitions",
|
||||||
|
return_value=_make_tool_defs("web_search"),
|
||||||
|
),
|
||||||
|
patch("run_agent.check_toolset_requirements", return_value={}),
|
||||||
|
patch("run_agent.OpenAI"),
|
||||||
|
):
|
||||||
|
AIAgent(
|
||||||
|
api_key="test-k...7890",
|
||||||
|
quiet_mode=True,
|
||||||
|
skip_context_files=True,
|
||||||
|
skip_memory=True,
|
||||||
|
)
|
||||||
|
AIAgent(
|
||||||
|
api_key="test-k...7890",
|
||||||
|
quiet_mode=True,
|
||||||
|
skip_context_files=True,
|
||||||
|
skip_memory=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
matching_handlers = [
|
||||||
|
handler for handler in root_logger.handlers
|
||||||
|
if isinstance(handler, RotatingFileHandler)
|
||||||
|
and error_log_path == Path(handler.baseFilename).resolve()
|
||||||
|
]
|
||||||
|
assert len(matching_handlers) == 1
|
||||||
|
finally:
|
||||||
|
for handler in list(root_logger.handlers):
|
||||||
|
root_logger.removeHandler(handler)
|
||||||
|
if handler not in original_handlers:
|
||||||
|
handler.close()
|
||||||
|
for handler in original_handlers:
|
||||||
|
root_logger.addHandler(handler)
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Helper to build mock assistant messages (API response objects)
|
# Helper to build mock assistant messages (API response objects)
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user