mirror of
https://github.com/home-assistant/core.git
synced 2026-07-04 05:05:38 +01:00
Allow disabling managed log file (#170374)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -66,6 +66,7 @@ from .const import (
|
||||
BASE_PLATFORMS,
|
||||
FORMAT_DATETIME,
|
||||
KEY_DATA_LOGGING as DATA_LOGGING,
|
||||
KEY_DATA_LOGGING_DISABLED_REASON as DATA_LOGGING_DISABLED_REASON,
|
||||
SIGNAL_BOOTSTRAP_INTEGRATIONS,
|
||||
)
|
||||
from .core_config import async_process_ha_core_config
|
||||
@@ -129,6 +130,11 @@ SETUP_ORDER_SORT_KEY = partial(contains, BASE_PLATFORMS)
|
||||
|
||||
|
||||
ERROR_LOG_FILENAME = "home-assistant.log"
|
||||
ENV_DISABLE_LOG_FILE = "HA_DISABLE_LOG_FILE"
|
||||
ENV_DUPLICATE_LOG_FILE = "HA_DUPLICATE_LOG_FILE"
|
||||
ENV_SUPERVISOR = "SUPERVISOR"
|
||||
LOG_FILE_DISABLED_REASON_ENVIRONMENT = "environment"
|
||||
LOG_FILE_DISABLED_REASON_SUPERVISOR = "supervisor"
|
||||
|
||||
# hass.data key for logging information.
|
||||
DATA_REGISTRIES_LOADED: HassKey[None] = HassKey("bootstrap_registries_loaded")
|
||||
@@ -642,10 +648,12 @@ async def async_enable_logging(
|
||||
logger.setLevel(logging.INFO if verbose else logging.WARNING)
|
||||
|
||||
if log_file is None:
|
||||
disabled_log_file_reason = _log_file_disabled_reason()
|
||||
default_log_path = hass.config.path(ERROR_LOG_FILENAME)
|
||||
if "SUPERVISOR" in os.environ and "HA_DUPLICATE_LOG_FILE" not in os.environ:
|
||||
if disabled_log_file_reason:
|
||||
# Rename the default log file if it exists, since previous versions created
|
||||
# it even on Supervisor
|
||||
# it before Supervisor disabled duplicate file logging or
|
||||
# HA_DISABLE_LOG_FILE disabled the log file.
|
||||
def rename_old_file() -> None:
|
||||
"""Rename old log file in executor."""
|
||||
if os.path.isfile(default_log_path):
|
||||
@@ -657,6 +665,7 @@ async def async_enable_logging(
|
||||
else:
|
||||
err_log_path = default_log_path
|
||||
else:
|
||||
disabled_log_file_reason = None
|
||||
err_log_path = os.path.abspath(log_file)
|
||||
|
||||
if err_log_path:
|
||||
@@ -669,10 +678,34 @@ async def async_enable_logging(
|
||||
|
||||
# Save the log file location for access by other components.
|
||||
hass.data[DATA_LOGGING] = err_log_path
|
||||
elif disabled_log_file_reason == LOG_FILE_DISABLED_REASON_ENVIRONMENT:
|
||||
hass.data[DATA_LOGGING_DISABLED_REASON] = disabled_log_file_reason
|
||||
|
||||
async_activate_log_queue_handler(hass)
|
||||
|
||||
|
||||
def _log_file_disabled_reason() -> str | None:
|
||||
"""Return why the log file is disabled."""
|
||||
if ENV_SUPERVISOR in os.environ and ENV_DUPLICATE_LOG_FILE not in os.environ:
|
||||
return LOG_FILE_DISABLED_REASON_SUPERVISOR
|
||||
|
||||
disable_log_file = os.environ.get(ENV_DISABLE_LOG_FILE)
|
||||
if disable_log_file is None:
|
||||
return None
|
||||
|
||||
try:
|
||||
if cv.boolean(disable_log_file):
|
||||
return LOG_FILE_DISABLED_REASON_ENVIRONMENT
|
||||
except vol.Invalid:
|
||||
_LOGGER.warning(
|
||||
"Ignoring invalid %s value: %s. Expected a boolean value: "
|
||||
"1/0, true/false, yes/no, on/off, or enable/disable",
|
||||
ENV_DISABLE_LOG_FILE,
|
||||
disable_log_file,
|
||||
)
|
||||
return None
|
||||
|
||||
|
||||
def _create_log_file(
|
||||
err_log_path: str, log_rotate_days: int | None
|
||||
) -> RotatingFileHandler | TimedRotatingFileHandler:
|
||||
@@ -734,7 +767,7 @@ def _get_domains(hass: core.HomeAssistant, config: dict[str, Any]) -> set[str]:
|
||||
domains.update(DEFAULT_INTEGRATIONS_RECOVERY_MODE)
|
||||
|
||||
# Add domains depending on if the Supervisor is used or not
|
||||
if "SUPERVISOR" in os.environ:
|
||||
if ENV_SUPERVISOR in os.environ:
|
||||
domains.update(DEFAULT_INTEGRATIONS_SUPERVISOR)
|
||||
|
||||
return domains
|
||||
|
||||
@@ -1031,8 +1031,9 @@ SIGNAL_BOOTSTRAP_INTEGRATIONS: SignalType[dict[str, float]] = SignalType(
|
||||
)
|
||||
|
||||
|
||||
# hass.data key for logging information.
|
||||
# hass.data keys for logging information.
|
||||
KEY_DATA_LOGGING: HassKey[str] = HassKey("logging")
|
||||
KEY_DATA_LOGGING_DISABLED_REASON: HassKey[str] = HassKey("logging_disabled_reason")
|
||||
|
||||
|
||||
# Date/Time formats
|
||||
|
||||
@@ -50,6 +50,7 @@ from .const import (
|
||||
CONF_URL,
|
||||
CONF_USERNAME,
|
||||
EVENT_CORE_CONFIG_UPDATE,
|
||||
KEY_DATA_LOGGING_DISABLED_REASON,
|
||||
LEGACY_CONF_WHITELIST_EXTERNAL_DIRS,
|
||||
UnitOfLength,
|
||||
__version__,
|
||||
@@ -698,6 +699,11 @@ class Config:
|
||||
"language": self.language,
|
||||
"latitude": self.latitude,
|
||||
"location_name": self.location_name,
|
||||
"logging": {
|
||||
"log_file_disabled_reason": self.hass.data.get(
|
||||
KEY_DATA_LOGGING_DISABLED_REASON
|
||||
),
|
||||
},
|
||||
"longitude": self.longitude,
|
||||
"radius": self.radius,
|
||||
"recovery_mode": self.recovery_mode,
|
||||
|
||||
+76
-10
@@ -131,25 +131,78 @@ async def test_async_enable_logging(
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("extra_env", "log_file_count", "old_log_file_count"),
|
||||
[({}, 0, 1), ({"HA_DUPLICATE_LOG_FILE": "1"}, 1, 0)],
|
||||
(
|
||||
"env",
|
||||
"log_file_count",
|
||||
"old_log_file_count",
|
||||
"data_logging",
|
||||
"data_logging_disabled_reason",
|
||||
),
|
||||
[
|
||||
pytest.param(
|
||||
{"SUPERVISOR": "1"},
|
||||
0,
|
||||
1,
|
||||
None,
|
||||
None,
|
||||
id="supervisor",
|
||||
),
|
||||
pytest.param(
|
||||
{"SUPERVISOR": "1", "HA_DUPLICATE_LOG_FILE": "1"},
|
||||
1,
|
||||
0,
|
||||
CONFIG_LOG_FILE,
|
||||
None,
|
||||
id="supervisor-duplicate-log-file",
|
||||
),
|
||||
pytest.param(
|
||||
{"HA_DISABLE_LOG_FILE": "1"},
|
||||
0,
|
||||
1,
|
||||
None,
|
||||
"environment",
|
||||
id="disable-log-file",
|
||||
),
|
||||
pytest.param(
|
||||
{"HA_DISABLE_LOG_FILE": "0"},
|
||||
1,
|
||||
0,
|
||||
CONFIG_LOG_FILE,
|
||||
None,
|
||||
id="disable-log-file-false",
|
||||
),
|
||||
pytest.param(
|
||||
{"HA_DISABLE_LOG_FILE": "invalid"},
|
||||
1,
|
||||
0,
|
||||
CONFIG_LOG_FILE,
|
||||
None,
|
||||
id="disable-log-file-invalid",
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_async_enable_logging_supervisor(
|
||||
async def test_async_enable_logging_log_file_disable_control(
|
||||
hass: HomeAssistant,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
extra_env: dict[str, str],
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
env: dict[str, str],
|
||||
log_file_count: int,
|
||||
old_log_file_count: int,
|
||||
data_logging: str | None,
|
||||
data_logging_disabled_reason: str | None,
|
||||
) -> None:
|
||||
"""Test the default log file is not created on Supervisor."""
|
||||
"""Test the default log file disable controls."""
|
||||
|
||||
# Ensure we start with a clean slate
|
||||
cleanup_log_files()
|
||||
assert len(glob.glob(CONFIG_LOG_FILE)) == 0
|
||||
assert len(glob.glob(ARG_LOG_FILE)) == 0
|
||||
|
||||
for env_var in ("SUPERVISOR", "HA_DUPLICATE_LOG_FILE", "HA_DISABLE_LOG_FILE"):
|
||||
monkeypatch.delenv(env_var, raising=False)
|
||||
for env_var, value in env.items():
|
||||
monkeypatch.setenv(env_var, value)
|
||||
|
||||
with (
|
||||
patch.dict(os.environ, {"SUPERVISOR": "1", **extra_env}),
|
||||
patch(
|
||||
"homeassistant.bootstrap.async_activate_log_queue_handler"
|
||||
) as mock_async_activate_log_queue_handler,
|
||||
@@ -157,11 +210,19 @@ async def test_async_enable_logging_supervisor(
|
||||
):
|
||||
await bootstrap.async_enable_logging(hass)
|
||||
assert len(glob.glob(CONFIG_LOG_FILE)) == log_file_count
|
||||
assert hass.data.get(bootstrap.DATA_LOGGING) == data_logging
|
||||
assert (
|
||||
hass.data.get(bootstrap.DATA_LOGGING_DISABLED_REASON)
|
||||
== data_logging_disabled_reason
|
||||
)
|
||||
assert hass.config.as_dict()["logging"] == {
|
||||
"log_file_disabled_reason": data_logging_disabled_reason,
|
||||
}
|
||||
mock_async_activate_log_queue_handler.assert_called_once()
|
||||
mock_async_activate_log_queue_handler.reset_mock()
|
||||
|
||||
# Check that if the log file exists, it is renamed
|
||||
def write_log_file():
|
||||
def write_log_file() -> None:
|
||||
with open(
|
||||
get_test_config_dir("home-assistant.log"), "w", encoding="utf8"
|
||||
) as f:
|
||||
@@ -174,6 +235,11 @@ async def test_async_enable_logging_supervisor(
|
||||
await bootstrap.async_enable_logging(hass)
|
||||
assert len(glob.glob(CONFIG_LOG_FILE)) == log_file_count
|
||||
assert len(glob.glob(f"{CONFIG_LOG_FILE}.old")) == old_log_file_count
|
||||
assert hass.data.get(bootstrap.DATA_LOGGING) == data_logging
|
||||
assert (
|
||||
hass.data.get(bootstrap.DATA_LOGGING_DISABLED_REASON)
|
||||
== data_logging_disabled_reason
|
||||
)
|
||||
mock_async_activate_log_queue_handler.assert_called_once()
|
||||
mock_async_activate_log_queue_handler.reset_mock()
|
||||
|
||||
@@ -183,9 +249,9 @@ async def test_async_enable_logging_supervisor(
|
||||
log_file="test.log",
|
||||
)
|
||||
mock_async_activate_log_queue_handler.assert_called_once()
|
||||
# Even on Supervisor, the log file should be created
|
||||
# if it is explicitly specified
|
||||
# The log file should be created if it is explicitly specified.
|
||||
assert len(glob.glob(ARG_LOG_FILE)) > 0
|
||||
assert bootstrap.DATA_LOGGING in hass.data
|
||||
|
||||
cleanup_log_files()
|
||||
|
||||
|
||||
@@ -928,6 +928,9 @@ async def test_config_as_dict() -> None:
|
||||
"currency": "EUR",
|
||||
"country": None,
|
||||
"language": "en",
|
||||
"logging": {
|
||||
"log_file_disabled_reason": None,
|
||||
},
|
||||
"safe_mode": False,
|
||||
"debug": False,
|
||||
"radius": 100,
|
||||
|
||||
Reference in New Issue
Block a user