From f683adc74cfebd9d30196fbbfe4a666275fe35d1 Mon Sep 17 00:00:00 2001 From: Soulter <905617992@qq.com> Date: Tue, 7 Jul 2026 00:03:39 +0800 Subject: [PATCH] chore: use fixed event loop diagnostic defaults --- astrbot/core/utils/event_loop_diagnostics.py | 154 ++----------------- tests/unit/test_event_loop_diagnostics.py | 70 ++------- 2 files changed, 26 insertions(+), 198 deletions(-) diff --git a/astrbot/core/utils/event_loop_diagnostics.py b/astrbot/core/utils/event_loop_diagnostics.py index d11df16f8..47f51c971 100644 --- a/astrbot/core/utils/event_loop_diagnostics.py +++ b/astrbot/core/utils/event_loop_diagnostics.py @@ -1,6 +1,5 @@ import asyncio import faulthandler -import os from dataclasses import dataclass from pathlib import Path from typing import TextIO @@ -8,19 +7,12 @@ from typing import TextIO from astrbot import logger from astrbot.core.utils.astrbot_path import get_astrbot_data_path -LAG_MONITOR_ENABLED_ENV = "ASTRBOT_EVENT_LOOP_LAG_MONITOR" -LAG_MONITOR_INTERVAL_ENV = "ASTRBOT_EVENT_LOOP_LAG_INTERVAL" -LAG_MONITOR_THRESHOLD_ENV = "ASTRBOT_EVENT_LOOP_LAG_THRESHOLD" -WATCHDOG_ENABLED_ENV = "ASTRBOT_EVENT_LOOP_WATCHDOG" -WATCHDOG_INTERVAL_ENV = "ASTRBOT_EVENT_LOOP_WATCHDOG_INTERVAL" -WATCHDOG_TIMEOUT_ENV = "ASTRBOT_EVENT_LOOP_WATCHDOG_TIMEOUT" -WATCHDOG_LOG_PATH_ENV = "ASTRBOT_EVENT_LOOP_WATCHDOG_LOG_PATH" -WATCHDOG_LOG_MAX_BYTES_ENV = "ASTRBOT_EVENT_LOOP_WATCHDOG_LOG_MAX_BYTES" - -DEFAULT_LAG_MONITOR_INTERVAL = 1.0 -DEFAULT_LAG_MONITOR_THRESHOLD = 5.0 -DEFAULT_WATCHDOG_INTERVAL = 1.0 -DEFAULT_WATCHDOG_TIMEOUT = 15.0 +DEFAULT_LAG_MONITOR_ENABLED = True +DEFAULT_LAG_MONITOR_INTERVAL = 5.0 +DEFAULT_LAG_MONITOR_THRESHOLD = 15.0 +DEFAULT_WATCHDOG_ENABLED = True +DEFAULT_WATCHDOG_INTERVAL = 5.0 +DEFAULT_WATCHDOG_TIMEOUT = 30.0 DEFAULT_WATCHDOG_LOG_RELATIVE_PATH = Path("logs") / "event_loop_watchdog.log" DEFAULT_WATCHDOG_LOG_MAX_BYTES = 1024 * 1024 @@ -50,146 +42,30 @@ class EventLoopDiagnosticSettings: watchdog_log_max_bytes: int -def _env_flag(name: str, default: bool) -> bool: - """Read a boolean flag from the environment. - - Args: - name: Environment variable name. - default: Value to use when the variable is unset or empty. - - Returns: - Parsed boolean value. - """ - value = os.environ.get(name) - if value is None or value.strip() == "": - return default - return value.strip().lower() in {"1", "true", "yes", "on"} - - -def _env_float(name: str, default: float, minimum: float) -> float: - """Read a bounded float from the environment. - - Args: - name: Environment variable name. - default: Value to use when parsing fails or the value is too small. - minimum: Smallest accepted value. - - Returns: - Parsed float value or the default. - """ - value = os.environ.get(name) - if value is None or value.strip() == "": - return default - try: - parsed = float(value) - except ValueError: - logger.warning( - "Invalid %s=%r, fallback to %.3fs.", - name, - value, - default, - ) - return default - if parsed < minimum: - logger.warning( - "Invalid %s=%r, expected at least %.3fs; fallback to %.3fs.", - name, - value, - minimum, - default, - ) - return default - return parsed - - -def _env_int(name: str, default: int, minimum: int) -> int: - """Read a bounded integer from the environment. - - Args: - name: Environment variable name. - default: Value to use when parsing fails or the value is too small. - minimum: Smallest accepted value. - - Returns: - Parsed integer value or the default. - """ - value = os.environ.get(name) - if value is None or value.strip() == "": - return default - try: - parsed = int(value) - except ValueError: - logger.warning( - "Invalid %s=%r, fallback to %d.", - name, - value, - default, - ) - return default - if parsed < minimum: - logger.warning( - "Invalid %s=%r, expected at least %d; fallback to %d.", - name, - value, - minimum, - default, - ) - return default - return parsed - - def _watchdog_log_path() -> Path: """Resolve the watchdog stack dump log path. Returns: Absolute path for watchdog stack dump output. """ - configured = os.environ.get(WATCHDOG_LOG_PATH_ENV, "").strip() - path = ( - Path(configured).expanduser() - if configured - else DEFAULT_WATCHDOG_LOG_RELATIVE_PATH - ) - if not path.is_absolute(): - path = Path(get_astrbot_data_path()) / path - return path + return Path(get_astrbot_data_path()) / DEFAULT_WATCHDOG_LOG_RELATIVE_PATH def load_event_loop_diagnostic_settings() -> EventLoopDiagnosticSettings: - """Load event loop diagnostic settings from environment variables. + """Load fixed event loop diagnostic settings. Returns: Event loop diagnostic settings. """ return EventLoopDiagnosticSettings( - lag_monitor_enabled=_env_flag(LAG_MONITOR_ENABLED_ENV, True), - lag_monitor_interval=_env_float( - LAG_MONITOR_INTERVAL_ENV, - DEFAULT_LAG_MONITOR_INTERVAL, - 0.1, - ), - lag_monitor_threshold=_env_float( - LAG_MONITOR_THRESHOLD_ENV, - DEFAULT_LAG_MONITOR_THRESHOLD, - 0.1, - ), - watchdog_enabled=_env_flag(WATCHDOG_ENABLED_ENV, False), - watchdog_interval=_env_float( - WATCHDOG_INTERVAL_ENV, - DEFAULT_WATCHDOG_INTERVAL, - 0.1, - ), - watchdog_timeout=_env_float( - WATCHDOG_TIMEOUT_ENV, - DEFAULT_WATCHDOG_TIMEOUT, - 1.0, - ), + lag_monitor_enabled=DEFAULT_LAG_MONITOR_ENABLED, + lag_monitor_interval=DEFAULT_LAG_MONITOR_INTERVAL, + lag_monitor_threshold=DEFAULT_LAG_MONITOR_THRESHOLD, + watchdog_enabled=DEFAULT_WATCHDOG_ENABLED, + watchdog_interval=DEFAULT_WATCHDOG_INTERVAL, + watchdog_timeout=DEFAULT_WATCHDOG_TIMEOUT, watchdog_log_path=_watchdog_log_path(), - watchdog_log_max_bytes=_env_int( - WATCHDOG_LOG_MAX_BYTES_ENV, - DEFAULT_WATCHDOG_LOG_MAX_BYTES, - 1024, - ), + watchdog_log_max_bytes=DEFAULT_WATCHDOG_LOG_MAX_BYTES, ) diff --git a/tests/unit/test_event_loop_diagnostics.py b/tests/unit/test_event_loop_diagnostics.py index 20af5d425..73275c5e9 100644 --- a/tests/unit/test_event_loop_diagnostics.py +++ b/tests/unit/test_event_loop_diagnostics.py @@ -5,77 +5,29 @@ import pytest from astrbot.core.utils import event_loop_diagnostics as diagnostics -def _clear_diagnostic_env(monkeypatch): - """Clear event loop diagnostic environment variables. - - Args: - monkeypatch: Pytest monkeypatch fixture. - """ - for name in ( - diagnostics.LAG_MONITOR_ENABLED_ENV, - diagnostics.LAG_MONITOR_INTERVAL_ENV, - diagnostics.LAG_MONITOR_THRESHOLD_ENV, - diagnostics.WATCHDOG_ENABLED_ENV, - diagnostics.WATCHDOG_INTERVAL_ENV, - diagnostics.WATCHDOG_TIMEOUT_ENV, - diagnostics.WATCHDOG_LOG_PATH_ENV, - diagnostics.WATCHDOG_LOG_MAX_BYTES_ENV, - ): - monkeypatch.delenv(name, raising=False) - - -def test_load_event_loop_diagnostic_settings_defaults(monkeypatch): - """Default settings enable low-overhead lag monitoring only.""" - _clear_diagnostic_env(monkeypatch) - +def test_load_event_loop_diagnostic_settings_defaults(): + """Default settings enable lag monitoring and stack dump watchdog.""" settings = diagnostics.load_event_loop_diagnostic_settings() assert settings.lag_monitor_enabled is True assert settings.lag_monitor_interval == diagnostics.DEFAULT_LAG_MONITOR_INTERVAL assert settings.lag_monitor_threshold == diagnostics.DEFAULT_LAG_MONITOR_THRESHOLD - assert settings.watchdog_enabled is False + assert settings.watchdog_enabled is True + assert settings.watchdog_interval == diagnostics.DEFAULT_WATCHDOG_INTERVAL + assert settings.watchdog_timeout == diagnostics.DEFAULT_WATCHDOG_TIMEOUT assert settings.watchdog_log_max_bytes == diagnostics.DEFAULT_WATCHDOG_LOG_MAX_BYTES -def test_load_event_loop_diagnostic_settings_from_env(monkeypatch): - """Environment variables override event loop diagnostic settings.""" - _clear_diagnostic_env(monkeypatch) - monkeypatch.setenv(diagnostics.LAG_MONITOR_ENABLED_ENV, "0") - monkeypatch.setenv(diagnostics.WATCHDOG_ENABLED_ENV, "yes") - monkeypatch.setenv(diagnostics.WATCHDOG_TIMEOUT_ENV, "30") - monkeypatch.setenv(diagnostics.WATCHDOG_LOG_PATH_ENV, "/tmp/astrbot-watchdog.log") - monkeypatch.setenv(diagnostics.WATCHDOG_LOG_MAX_BYTES_ENV, "2048") - - settings = diagnostics.load_event_loop_diagnostic_settings() - - assert settings.lag_monitor_enabled is False - assert settings.watchdog_enabled is True - assert settings.watchdog_timeout == 30 - assert settings.watchdog_log_path.as_posix() == "/tmp/astrbot-watchdog.log" - assert settings.watchdog_log_max_bytes == 2048 - - @pytest.mark.asyncio -async def test_create_event_loop_diagnostic_tasks_respects_env(monkeypatch): - """Disabled lag monitor and watchdog should create no tasks.""" - _clear_diagnostic_env(monkeypatch) - monkeypatch.setenv(diagnostics.LAG_MONITOR_ENABLED_ENV, "false") - monkeypatch.setenv(diagnostics.WATCHDOG_ENABLED_ENV, "false") - - tasks = diagnostics.create_event_loop_diagnostic_tasks() - - assert tasks == [] - - -@pytest.mark.asyncio -async def test_create_event_loop_diagnostic_tasks_default_lag_monitor(monkeypatch): - """Default diagnostics should create only the lag monitor task.""" - _clear_diagnostic_env(monkeypatch) - +async def test_create_event_loop_diagnostic_tasks_defaults(): + """Default diagnostics should create both event loop diagnostic tasks.""" tasks = diagnostics.create_event_loop_diagnostic_tasks() try: - assert [task.get_name() for task in tasks] == ["event_loop_lag_monitor"] + assert [task.get_name() for task in tasks] == [ + "event_loop_lag_monitor", + "event_loop_faulthandler_watchdog", + ] finally: for task in tasks: task.cancel()