From 1578bb1dfcab5339052a9b6e4039fa41c76745c0 Mon Sep 17 00:00:00 2001 From: abmantis Date: Wed, 10 Dec 2025 18:38:26 +0000 Subject: [PATCH 1/4] Enable log file on supervised when HA_DUPLICATE_LOG_FILE env var is set --- homeassistant/bootstrap.py | 7 +++++-- tests/test_bootstrap.py | 19 ++++++++++++++----- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/homeassistant/bootstrap.py b/homeassistant/bootstrap.py index 9e375c7fdb8e2e..59bc0ffbefbcc4 100644 --- a/homeassistant/bootstrap.py +++ b/homeassistant/bootstrap.py @@ -624,8 +624,11 @@ async def async_enable_logging( if log_file is None: default_log_path = hass.config.path(ERROR_LOG_FILENAME) - if "SUPERVISOR" in os.environ: - _LOGGER.info("Running in Supervisor, not logging to file") + if "SUPERVISOR" in os.environ and "HA_DUPLICATE_LOG_FILE" not in os.environ: + _LOGGER.info( + "Running in Supervisor without the duplicate log option," + "not logging to file" + ) # Rename the default log file if it exists, since previous versions created # it even on Supervisor if os.path.isfile(default_log_path): diff --git a/tests/test_bootstrap.py b/tests/test_bootstrap.py index 8f6a59a29156ce..e3d32354e49463 100644 --- a/tests/test_bootstrap.py +++ b/tests/test_bootstrap.py @@ -130,8 +130,16 @@ async def test_async_enable_logging( cleanup_log_files() +@pytest.mark.parametrize( + ("extra_env", "log_file_count", "old_log_file_count"), + [({}, 0, 1), ({"HA_DUPLICATE_LOG_FILE": "1"}, 1, 0)], +) async def test_async_enable_logging_supervisor( - hass: HomeAssistant, caplog: pytest.LogCaptureFixture + hass: HomeAssistant, + caplog: pytest.LogCaptureFixture, + extra_env: dict[str, str], + log_file_count: int, + old_log_file_count: int, ) -> None: """Test to ensure the default log file is not created on Supervisor installations.""" @@ -141,14 +149,14 @@ async def test_async_enable_logging_supervisor( assert len(glob.glob(ARG_LOG_FILE)) == 0 with ( - patch.dict(os.environ, {"SUPERVISOR": "1"}), + patch.dict(os.environ, {"SUPERVISOR": "1", **extra_env}), patch( "homeassistant.bootstrap.async_activate_log_queue_handler" ) as mock_async_activate_log_queue_handler, patch("logging.getLogger"), ): await bootstrap.async_enable_logging(hass) - assert len(glob.glob(CONFIG_LOG_FILE)) == 0 + assert len(glob.glob(CONFIG_LOG_FILE)) == log_file_count mock_async_activate_log_queue_handler.assert_called_once() mock_async_activate_log_queue_handler.reset_mock() @@ -162,9 +170,10 @@ def write_log_file(): await hass.async_add_executor_job(write_log_file) assert len(glob.glob(CONFIG_LOG_FILE)) == 1 assert len(glob.glob(f"{CONFIG_LOG_FILE}.old")) == 0 + await bootstrap.async_enable_logging(hass) - assert len(glob.glob(CONFIG_LOG_FILE)) == 0 - assert len(glob.glob(f"{CONFIG_LOG_FILE}.old")) == 1 + assert len(glob.glob(CONFIG_LOG_FILE)) == log_file_count + assert len(glob.glob(f"{CONFIG_LOG_FILE}.old")) == old_log_file_count mock_async_activate_log_queue_handler.assert_called_once() mock_async_activate_log_queue_handler.reset_mock() From f88129ed117598d4b1fb238478f694b498aac193 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ab=C3=ADlio=20Costa?= Date: Thu, 11 Dec 2025 11:47:12 +0000 Subject: [PATCH 2/4] Apply suggestion from @abmantis --- homeassistant/bootstrap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/bootstrap.py b/homeassistant/bootstrap.py index 59bc0ffbefbcc4..5df18f182b9516 100644 --- a/homeassistant/bootstrap.py +++ b/homeassistant/bootstrap.py @@ -626,7 +626,7 @@ async def async_enable_logging( default_log_path = hass.config.path(ERROR_LOG_FILENAME) if "SUPERVISOR" in os.environ and "HA_DUPLICATE_LOG_FILE" not in os.environ: _LOGGER.info( - "Running in Supervisor without the duplicate log option," + "Running in Supervisor without the duplicate log option, " "not logging to file" ) # Rename the default log file if it exists, since previous versions created From b5142754fcbb6dc991f602159c8f7d6c6dad56d8 Mon Sep 17 00:00:00 2001 From: abmantis Date: Mon, 15 Dec 2025 17:10:46 +0000 Subject: [PATCH 3/4] Remove log; move io out of the event loop --- homeassistant/bootstrap.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/homeassistant/bootstrap.py b/homeassistant/bootstrap.py index 5df18f182b9516..5b2454c90f162c 100644 --- a/homeassistant/bootstrap.py +++ b/homeassistant/bootstrap.py @@ -625,15 +625,13 @@ async def async_enable_logging( if log_file is None: default_log_path = hass.config.path(ERROR_LOG_FILENAME) if "SUPERVISOR" in os.environ and "HA_DUPLICATE_LOG_FILE" not in os.environ: - _LOGGER.info( - "Running in Supervisor without the duplicate log option, " - "not logging to file" - ) # Rename the default log file if it exists, since previous versions created # it even on Supervisor if os.path.isfile(default_log_path): with contextlib.suppress(OSError): - os.rename(default_log_path, f"{default_log_path}.old") + await hass.async_add_executor_job( + os.rename, default_log_path, f"{default_log_path}.old" + ) err_log_path = None else: err_log_path = default_log_path From fadc2526619a7b4fb436b9d0a2057bdac3e883fb Mon Sep 17 00:00:00 2001 From: abmantis Date: Tue, 16 Dec 2025 13:01:52 +0000 Subject: [PATCH 4/4] Move both check and rename to executor --- homeassistant/bootstrap.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/homeassistant/bootstrap.py b/homeassistant/bootstrap.py index 5b2454c90f162c..96af205e4e0aa3 100644 --- a/homeassistant/bootstrap.py +++ b/homeassistant/bootstrap.py @@ -627,11 +627,13 @@ async def async_enable_logging( if "SUPERVISOR" in os.environ and "HA_DUPLICATE_LOG_FILE" not in os.environ: # Rename the default log file if it exists, since previous versions created # it even on Supervisor - if os.path.isfile(default_log_path): - with contextlib.suppress(OSError): - await hass.async_add_executor_job( - os.rename, default_log_path, f"{default_log_path}.old" - ) + def rename_old_file() -> None: + """Rename old log file in executor.""" + if os.path.isfile(default_log_path): + with contextlib.suppress(OSError): + os.rename(default_log_path, f"{default_log_path}.old") + + await hass.async_add_executor_job(rename_old_file) err_log_path = None else: err_log_path = default_log_path