Skip to content

Commit 07da0cf

Browse files
Stop writing to config dir log file on supervised install (#146675)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
1 parent b411a11 commit 07da0cf

File tree

2 files changed

+98
-35
lines changed

2 files changed

+98
-35
lines changed

homeassistant/bootstrap.py

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -616,34 +616,44 @@ async def async_enable_logging(
616616
),
617617
)
618618

619-
# Log errors to a file if we have write access to file or config dir
619+
logger = logging.getLogger()
620+
logger.setLevel(logging.INFO if verbose else logging.WARNING)
621+
620622
if log_file is None:
621-
err_log_path = hass.config.path(ERROR_LOG_FILENAME)
623+
default_log_path = hass.config.path(ERROR_LOG_FILENAME)
624+
if "SUPERVISOR" in os.environ:
625+
_LOGGER.info("Running in Supervisor, not logging to file")
626+
# Rename the default log file if it exists, since previous versions created
627+
# it even on Supervisor
628+
if os.path.isfile(default_log_path):
629+
with contextlib.suppress(OSError):
630+
os.rename(default_log_path, f"{default_log_path}.old")
631+
err_log_path = None
632+
else:
633+
err_log_path = default_log_path
622634
else:
623635
err_log_path = os.path.abspath(log_file)
624636

625-
err_path_exists = os.path.isfile(err_log_path)
626-
err_dir = os.path.dirname(err_log_path)
627-
628-
# Check if we can write to the error log if it exists or that
629-
# we can create files in the containing directory if not.
630-
if (err_path_exists and os.access(err_log_path, os.W_OK)) or (
631-
not err_path_exists and os.access(err_dir, os.W_OK)
632-
):
633-
err_handler = await hass.async_add_executor_job(
634-
_create_log_file, err_log_path, log_rotate_days
635-
)
637+
if err_log_path:
638+
err_path_exists = os.path.isfile(err_log_path)
639+
err_dir = os.path.dirname(err_log_path)
636640

637-
err_handler.setFormatter(logging.Formatter(fmt, datefmt=FORMAT_DATETIME))
641+
# Check if we can write to the error log if it exists or that
642+
# we can create files in the containing directory if not.
643+
if (err_path_exists and os.access(err_log_path, os.W_OK)) or (
644+
not err_path_exists and os.access(err_dir, os.W_OK)
645+
):
646+
err_handler = await hass.async_add_executor_job(
647+
_create_log_file, err_log_path, log_rotate_days
648+
)
638649

639-
logger = logging.getLogger()
640-
logger.addHandler(err_handler)
641-
logger.setLevel(logging.INFO if verbose else logging.WARNING)
650+
err_handler.setFormatter(logging.Formatter(fmt, datefmt=FORMAT_DATETIME))
651+
logger.addHandler(err_handler)
642652

643-
# Save the log file location for access by other components.
644-
hass.data[DATA_LOGGING] = err_log_path
645-
else:
646-
_LOGGER.error("Unable to set up error log %s (access denied)", err_log_path)
653+
# Save the log file location for access by other components.
654+
hass.data[DATA_LOGGING] = err_log_path
655+
else:
656+
_LOGGER.error("Unable to set up error log %s (access denied)", err_log_path)
647657

648658
async_activate_log_queue_handler(hass)
649659

tests/test_bootstrap.py

Lines changed: 67 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,17 @@
3838

3939
VERSION_PATH = os.path.join(get_test_config_dir(), config_util.VERSION_FILE)
4040

41+
CONFIG_LOG_FILE = get_test_config_dir("home-assistant.log")
42+
ARG_LOG_FILE = "test.log"
43+
44+
45+
def cleanup_log_files() -> None:
46+
"""Remove all log files."""
47+
for f in glob.glob(f"{CONFIG_LOG_FILE}*"):
48+
os.remove(f)
49+
for f in glob.glob(f"{ARG_LOG_FILE}*"):
50+
os.remove(f)
51+
4152

4253
@pytest.fixture(autouse=True)
4354
def disable_installed_check() -> Generator[None]:
@@ -85,16 +96,11 @@ async def test_async_enable_logging(
8596
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
8697
) -> None:
8798
"""Test to ensure logging is migrated to the queue handlers."""
88-
config_log_file_pattern = get_test_config_dir("home-assistant.log*")
89-
arg_log_file_pattern = "test.log*"
9099

91100
# Ensure we start with a clean slate
92-
for f in glob.glob(arg_log_file_pattern):
93-
os.remove(f)
94-
for f in glob.glob(config_log_file_pattern):
95-
os.remove(f)
96-
assert len(glob.glob(config_log_file_pattern)) == 0
97-
assert len(glob.glob(arg_log_file_pattern)) == 0
101+
cleanup_log_files()
102+
assert len(glob.glob(CONFIG_LOG_FILE)) == 0
103+
assert len(glob.glob(ARG_LOG_FILE)) == 0
98104

99105
with (
100106
patch("logging.getLogger"),
@@ -108,7 +114,7 @@ async def test_async_enable_logging(
108114
):
109115
await bootstrap.async_enable_logging(hass)
110116
mock_async_activate_log_queue_handler.assert_called_once()
111-
assert len(glob.glob(config_log_file_pattern)) > 0
117+
assert len(glob.glob(CONFIG_LOG_FILE)) > 0
112118

113119
mock_async_activate_log_queue_handler.reset_mock()
114120
await bootstrap.async_enable_logging(
@@ -117,14 +123,61 @@ async def test_async_enable_logging(
117123
log_file="test.log",
118124
)
119125
mock_async_activate_log_queue_handler.assert_called_once()
120-
assert len(glob.glob(arg_log_file_pattern)) > 0
126+
assert len(glob.glob(ARG_LOG_FILE)) > 0
121127

122128
assert "Error rolling over log file" in caplog.text
123129

124-
for f in glob.glob(arg_log_file_pattern):
125-
os.remove(f)
126-
for f in glob.glob(config_log_file_pattern):
127-
os.remove(f)
130+
cleanup_log_files()
131+
132+
133+
async def test_async_enable_logging_supervisor(
134+
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
135+
) -> None:
136+
"""Test to ensure the default log file is not created on Supervisor installations."""
137+
138+
# Ensure we start with a clean slate
139+
cleanup_log_files()
140+
assert len(glob.glob(CONFIG_LOG_FILE)) == 0
141+
assert len(glob.glob(ARG_LOG_FILE)) == 0
142+
143+
with (
144+
patch.dict(os.environ, {"SUPERVISOR": "1"}),
145+
patch(
146+
"homeassistant.bootstrap.async_activate_log_queue_handler"
147+
) as mock_async_activate_log_queue_handler,
148+
patch("logging.getLogger"),
149+
):
150+
await bootstrap.async_enable_logging(hass)
151+
assert len(glob.glob(CONFIG_LOG_FILE)) == 0
152+
mock_async_activate_log_queue_handler.assert_called_once()
153+
mock_async_activate_log_queue_handler.reset_mock()
154+
155+
# Check that if the log file exists, it is renamed
156+
def write_log_file():
157+
with open(
158+
get_test_config_dir("home-assistant.log"), "w", encoding="utf8"
159+
) as f:
160+
f.write("test")
161+
162+
await hass.async_add_executor_job(write_log_file)
163+
assert len(glob.glob(CONFIG_LOG_FILE)) == 1
164+
assert len(glob.glob(f"{CONFIG_LOG_FILE}.old")) == 0
165+
await bootstrap.async_enable_logging(hass)
166+
assert len(glob.glob(CONFIG_LOG_FILE)) == 0
167+
assert len(glob.glob(f"{CONFIG_LOG_FILE}.old")) == 1
168+
mock_async_activate_log_queue_handler.assert_called_once()
169+
mock_async_activate_log_queue_handler.reset_mock()
170+
171+
await bootstrap.async_enable_logging(
172+
hass,
173+
log_rotate_days=5,
174+
log_file="test.log",
175+
)
176+
mock_async_activate_log_queue_handler.assert_called_once()
177+
# Even on Supervisor, the log file should be created if it is explicitly specified
178+
assert len(glob.glob(ARG_LOG_FILE)) > 0
179+
180+
cleanup_log_files()
128181

129182

130183
async def test_load_hassio(hass: HomeAssistant) -> None:

0 commit comments

Comments
 (0)