mirror of
https://github.com/home-assistant/core.git
synced 2025-12-24 21:06:19 +00:00
Provide api to see which integrations are being loaded (#48274)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
This commit is contained in:
@@ -20,14 +20,17 @@ from homeassistant.components import http
|
||||
from homeassistant.const import REQUIRED_NEXT_PYTHON_DATE, REQUIRED_NEXT_PYTHON_VER
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import area_registry, device_registry, entity_registry
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
from homeassistant.setup import (
|
||||
DATA_SETUP,
|
||||
DATA_SETUP_STARTED,
|
||||
DATA_SETUP_TIME,
|
||||
async_set_domains_to_be_loaded,
|
||||
async_setup_component,
|
||||
)
|
||||
from homeassistant.util.async_ import gather_with_concurrency
|
||||
import homeassistant.util.dt as dt_util
|
||||
from homeassistant.util.logging import async_activate_log_queue_handler
|
||||
from homeassistant.util.package import async_get_user_site, is_virtual_env
|
||||
|
||||
@@ -42,6 +45,8 @@ ERROR_LOG_FILENAME = "home-assistant.log"
|
||||
DATA_LOGGING = "logging"
|
||||
|
||||
LOG_SLOW_STARTUP_INTERVAL = 60
|
||||
SLOW_STARTUP_CHECK_INTERVAL = 1
|
||||
SIGNAL_BOOTSTRAP_INTEGRATONS = "bootstrap_integrations"
|
||||
|
||||
STAGE_1_TIMEOUT = 120
|
||||
STAGE_2_TIMEOUT = 300
|
||||
@@ -380,19 +385,29 @@ def _get_domains(hass: core.HomeAssistant, config: dict[str, Any]) -> set[str]:
|
||||
return domains
|
||||
|
||||
|
||||
async def _async_log_pending_setups(
|
||||
hass: core.HomeAssistant, domains: set[str], setup_started: dict[str, datetime]
|
||||
) -> None:
|
||||
async def _async_watch_pending_setups(hass: core.HomeAssistant) -> None:
|
||||
"""Periodic log of setups that are pending for longer than LOG_SLOW_STARTUP_INTERVAL."""
|
||||
loop_count = 0
|
||||
setup_started: dict[str, datetime] = hass.data[DATA_SETUP_STARTED]
|
||||
while True:
|
||||
await asyncio.sleep(LOG_SLOW_STARTUP_INTERVAL)
|
||||
remaining = [domain for domain in domains if domain in setup_started]
|
||||
now = dt_util.utcnow()
|
||||
remaining_with_setup_started = {
|
||||
domain: (now - setup_started[domain]).total_seconds()
|
||||
for domain in setup_started
|
||||
}
|
||||
_LOGGER.debug("Integration remaining: %s", remaining_with_setup_started)
|
||||
async_dispatcher_send(
|
||||
hass, SIGNAL_BOOTSTRAP_INTEGRATONS, remaining_with_setup_started
|
||||
)
|
||||
await asyncio.sleep(SLOW_STARTUP_CHECK_INTERVAL)
|
||||
loop_count += SLOW_STARTUP_CHECK_INTERVAL
|
||||
|
||||
if remaining:
|
||||
if loop_count >= LOG_SLOW_STARTUP_INTERVAL and setup_started:
|
||||
_LOGGER.warning(
|
||||
"Waiting on integrations to complete setup: %s",
|
||||
", ".join(remaining),
|
||||
", ".join(setup_started),
|
||||
)
|
||||
loop_count = 0
|
||||
_LOGGER.debug("Running timeout Zones: %s", hass.timeout.zones)
|
||||
|
||||
|
||||
@@ -400,18 +415,13 @@ async def async_setup_multi_components(
|
||||
hass: core.HomeAssistant,
|
||||
domains: set[str],
|
||||
config: dict[str, Any],
|
||||
setup_started: dict[str, datetime],
|
||||
) -> None:
|
||||
"""Set up multiple domains. Log on failure."""
|
||||
futures = {
|
||||
domain: hass.async_create_task(async_setup_component(hass, domain, config))
|
||||
for domain in domains
|
||||
}
|
||||
log_task = asyncio.create_task(
|
||||
_async_log_pending_setups(hass, domains, setup_started)
|
||||
)
|
||||
await asyncio.wait(futures.values())
|
||||
log_task.cancel()
|
||||
errors = [domain for domain in domains if futures[domain].exception()]
|
||||
for domain in errors:
|
||||
exception = futures[domain].exception()
|
||||
@@ -427,7 +437,11 @@ async def _async_set_up_integrations(
|
||||
hass: core.HomeAssistant, config: dict[str, Any]
|
||||
) -> None:
|
||||
"""Set up all the integrations."""
|
||||
setup_started = hass.data[DATA_SETUP_STARTED] = {}
|
||||
hass.data[DATA_SETUP_STARTED] = {}
|
||||
setup_time = hass.data[DATA_SETUP_TIME] = {}
|
||||
|
||||
log_task = asyncio.create_task(_async_watch_pending_setups(hass))
|
||||
|
||||
domains_to_setup = _get_domains(hass, config)
|
||||
|
||||
# Resolve all dependencies so we know all integrations
|
||||
@@ -476,14 +490,14 @@ async def _async_set_up_integrations(
|
||||
# Load logging as soon as possible
|
||||
if logging_domains:
|
||||
_LOGGER.info("Setting up logging: %s", logging_domains)
|
||||
await async_setup_multi_components(hass, logging_domains, config, setup_started)
|
||||
await async_setup_multi_components(hass, logging_domains, config)
|
||||
|
||||
# Start up debuggers. Start these first in case they want to wait.
|
||||
debuggers = domains_to_setup & DEBUGGER_INTEGRATIONS
|
||||
|
||||
if debuggers:
|
||||
_LOGGER.debug("Setting up debuggers: %s", debuggers)
|
||||
await async_setup_multi_components(hass, debuggers, config, setup_started)
|
||||
await async_setup_multi_components(hass, debuggers, config)
|
||||
|
||||
# calculate what components to setup in what stage
|
||||
stage_1_domains = set()
|
||||
@@ -524,9 +538,7 @@ async def _async_set_up_integrations(
|
||||
async with hass.timeout.async_timeout(
|
||||
STAGE_1_TIMEOUT, cool_down=COOLDOWN_TIME
|
||||
):
|
||||
await async_setup_multi_components(
|
||||
hass, stage_1_domains, config, setup_started
|
||||
)
|
||||
await async_setup_multi_components(hass, stage_1_domains, config)
|
||||
except asyncio.TimeoutError:
|
||||
_LOGGER.warning("Setup timed out for stage 1 - moving forward")
|
||||
|
||||
@@ -539,12 +551,21 @@ async def _async_set_up_integrations(
|
||||
async with hass.timeout.async_timeout(
|
||||
STAGE_2_TIMEOUT, cool_down=COOLDOWN_TIME
|
||||
):
|
||||
await async_setup_multi_components(
|
||||
hass, stage_2_domains, config, setup_started
|
||||
)
|
||||
await async_setup_multi_components(hass, stage_2_domains, config)
|
||||
except asyncio.TimeoutError:
|
||||
_LOGGER.warning("Setup timed out for stage 2 - moving forward")
|
||||
|
||||
log_task.cancel()
|
||||
_LOGGER.debug(
|
||||
"Integration setup times: %s",
|
||||
{
|
||||
integration: timedelta.total_seconds()
|
||||
for integration, timedelta in sorted(
|
||||
setup_time.items(), key=lambda item: item[1].total_seconds() # type: ignore
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
# Wrap up startup
|
||||
_LOGGER.debug("Waiting for startup to wrap up")
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user