mirror of
https://github.com/home-assistant/core.git
synced 2025-04-23 16:57:53 +00:00
Remove http dependency from system log (#64386)
This commit is contained in:
parent
a8c14835b9
commit
7943b0ff5e
@ -8,7 +8,7 @@ import traceback
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant import __path__ as HOMEASSISTANT_PATH
|
||||
from homeassistant.components.http import HomeAssistantView
|
||||
from homeassistant.components import websocket_api
|
||||
from homeassistant.const import EVENT_HOMEASSISTANT_CLOSE, EVENT_HOMEASSISTANT_STOP
|
||||
from homeassistant.core import HomeAssistant, ServiceCall, callback
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
@ -224,7 +224,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
|
||||
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_CLOSE, _async_stop_queue_handler)
|
||||
|
||||
hass.http.register_view(AllErrorsView(handler))
|
||||
websocket_api.async_register_command(hass, list_errors)
|
||||
|
||||
async def async_service_handler(service: ServiceCall) -> None:
|
||||
"""Handle logger services."""
|
||||
@ -255,16 +255,14 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
class AllErrorsView(HomeAssistantView):
|
||||
"""Get all logged errors and warnings."""
|
||||
|
||||
url = "/api/error/all"
|
||||
name = "api:error:all"
|
||||
|
||||
def __init__(self, handler):
|
||||
"""Initialize a new AllErrorsView."""
|
||||
self.handler = handler
|
||||
|
||||
async def get(self, request):
|
||||
"""Get all errors and warnings."""
|
||||
return self.json(self.handler.records.to_list())
|
||||
@websocket_api.require_admin
|
||||
@websocket_api.websocket_command({vol.Required("type"): "system_log/list"})
|
||||
@callback
|
||||
def list_errors(
|
||||
hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict
|
||||
):
|
||||
"""List all possible diagnostic handlers."""
|
||||
connection.send_result(
|
||||
msg["id"],
|
||||
hass.data[DOMAIN].records.to_list(),
|
||||
)
|
||||
|
@ -2,7 +2,7 @@
|
||||
"domain": "system_log",
|
||||
"name": "System Log",
|
||||
"documentation": "https://www.home-assistant.io/integrations/system_log",
|
||||
"dependencies": ["http"],
|
||||
"dependencies": [],
|
||||
"codeowners": [],
|
||||
"quality_scale": "internal"
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
"""Test system log component."""
|
||||
import asyncio
|
||||
from http import HTTPStatus
|
||||
import logging
|
||||
import queue
|
||||
from unittest.mock import MagicMock, patch
|
||||
@ -11,6 +10,8 @@ from homeassistant.bootstrap import async_setup_component
|
||||
from homeassistant.components import system_log
|
||||
from homeassistant.core import callback
|
||||
|
||||
from tests.common import async_capture_events
|
||||
|
||||
_LOGGER = logging.getLogger("test_logger")
|
||||
BASIC_CONFIG = {"system_log": {"max_entries": 2}}
|
||||
|
||||
@ -34,19 +35,19 @@ async def _async_block_until_queue_empty(hass, sq):
|
||||
hass.data[system_log.DOMAIN].acquire()
|
||||
hass.data[system_log.DOMAIN].release()
|
||||
await hass.async_block_till_done()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
||||
async def get_error_log(hass, hass_client, expected_count):
|
||||
async def get_error_log(hass_ws_client):
|
||||
"""Fetch all entries from system_log via the API."""
|
||||
client = await hass_ws_client()
|
||||
await client.send_json({"id": 5, "type": "system_log/list"})
|
||||
|
||||
client = await hass_client()
|
||||
resp = await client.get("/api/error/all")
|
||||
assert resp.status == HTTPStatus.OK
|
||||
msg = await client.receive_json()
|
||||
|
||||
data = await resp.json()
|
||||
|
||||
assert len(data) == expected_count
|
||||
return data
|
||||
assert msg["id"] == 5
|
||||
assert msg["success"]
|
||||
return msg["result"]
|
||||
|
||||
|
||||
def _generate_and_log_exception(exception, log):
|
||||
@ -56,6 +57,18 @@ def _generate_and_log_exception(exception, log):
|
||||
_LOGGER.exception(log)
|
||||
|
||||
|
||||
def find_log(logs, level):
|
||||
"""Return log with specific level."""
|
||||
if not isinstance(level, tuple):
|
||||
level = (level,)
|
||||
log = next(
|
||||
(log for log in logs if log["level"] in level),
|
||||
None,
|
||||
)
|
||||
assert log is not None
|
||||
return log
|
||||
|
||||
|
||||
def assert_log(log, exception, message, level):
|
||||
"""Assert that specified values are in a specific log entry."""
|
||||
if not isinstance(message, list):
|
||||
@ -73,7 +86,7 @@ def get_frame(name):
|
||||
return (name, 5, None, None)
|
||||
|
||||
|
||||
async def test_normal_logs(hass, simple_queue, hass_client):
|
||||
async def test_normal_logs(hass, simple_queue, hass_ws_client):
|
||||
"""Test that debug and info are not logged."""
|
||||
await async_setup_component(hass, system_log.DOMAIN, BASIC_CONFIG)
|
||||
|
||||
@ -82,36 +95,37 @@ async def test_normal_logs(hass, simple_queue, hass_client):
|
||||
await _async_block_until_queue_empty(hass, simple_queue)
|
||||
|
||||
# Assert done by get_error_log
|
||||
await get_error_log(hass, hass_client, 0)
|
||||
logs = await get_error_log(hass_ws_client)
|
||||
assert len([msg for msg in logs if msg["level"] in ("DEBUG", "INFO")]) == 0
|
||||
|
||||
|
||||
async def test_exception(hass, simple_queue, hass_client):
|
||||
async def test_exception(hass, simple_queue, hass_ws_client):
|
||||
"""Test that exceptions are logged and retrieved correctly."""
|
||||
await async_setup_component(hass, system_log.DOMAIN, BASIC_CONFIG)
|
||||
_generate_and_log_exception("exception message", "log message")
|
||||
await _async_block_until_queue_empty(hass, simple_queue)
|
||||
|
||||
log = (await get_error_log(hass, hass_client, 1))[0]
|
||||
log = find_log(await get_error_log(hass_ws_client), "ERROR")
|
||||
assert log is not None
|
||||
assert_log(log, "exception message", "log message", "ERROR")
|
||||
|
||||
|
||||
async def test_warning(hass, simple_queue, hass_client):
|
||||
async def test_warning(hass, simple_queue, hass_ws_client):
|
||||
"""Test that warning are logged and retrieved correctly."""
|
||||
await async_setup_component(hass, system_log.DOMAIN, BASIC_CONFIG)
|
||||
_LOGGER.warning("warning message")
|
||||
await _async_block_until_queue_empty(hass, simple_queue)
|
||||
|
||||
log = (await get_error_log(hass, hass_client, 1))[0]
|
||||
log = find_log(await get_error_log(hass_ws_client), "WARNING")
|
||||
assert_log(log, "", "warning message", "WARNING")
|
||||
|
||||
|
||||
async def test_error(hass, simple_queue, hass_client):
|
||||
async def test_error(hass, simple_queue, hass_ws_client):
|
||||
"""Test that errors are logged and retrieved correctly."""
|
||||
await async_setup_component(hass, system_log.DOMAIN, BASIC_CONFIG)
|
||||
_LOGGER.error("error message")
|
||||
await _async_block_until_queue_empty(hass, simple_queue)
|
||||
|
||||
log = (await get_error_log(hass, hass_client, 1))[0]
|
||||
log = find_log(await get_error_log(hass_ws_client), "ERROR")
|
||||
assert_log(log, "", "error message", "ERROR")
|
||||
|
||||
|
||||
@ -138,14 +152,7 @@ async def test_error_posted_as_event(hass, simple_queue):
|
||||
await async_setup_component(
|
||||
hass, system_log.DOMAIN, {"system_log": {"max_entries": 2, "fire_event": True}}
|
||||
)
|
||||
events = []
|
||||
|
||||
@callback
|
||||
def event_listener(event):
|
||||
"""Listen to events of type system_log_event."""
|
||||
events.append(event)
|
||||
|
||||
hass.bus.async_listen(system_log.EVENT_SYSTEM_LOG, event_listener)
|
||||
events = async_capture_events(hass, system_log.EVENT_SYSTEM_LOG)
|
||||
|
||||
_LOGGER.error("error message")
|
||||
await _async_block_until_queue_empty(hass, simple_queue)
|
||||
@ -154,17 +161,17 @@ async def test_error_posted_as_event(hass, simple_queue):
|
||||
assert_log(events[0].data, "", "error message", "ERROR")
|
||||
|
||||
|
||||
async def test_critical(hass, simple_queue, hass_client):
|
||||
async def test_critical(hass, simple_queue, hass_ws_client):
|
||||
"""Test that critical are logged and retrieved correctly."""
|
||||
await async_setup_component(hass, system_log.DOMAIN, BASIC_CONFIG)
|
||||
_LOGGER.critical("critical message")
|
||||
await _async_block_until_queue_empty(hass, simple_queue)
|
||||
|
||||
log = (await get_error_log(hass, hass_client, 1))[0]
|
||||
log = find_log(await get_error_log(hass_ws_client), "CRITICAL")
|
||||
assert_log(log, "", "critical message", "CRITICAL")
|
||||
|
||||
|
||||
async def test_remove_older_logs(hass, simple_queue, hass_client):
|
||||
async def test_remove_older_logs(hass, simple_queue, hass_ws_client):
|
||||
"""Test that older logs are rotated out."""
|
||||
await async_setup_component(hass, system_log.DOMAIN, BASIC_CONFIG)
|
||||
_LOGGER.error("error message 1")
|
||||
@ -172,7 +179,7 @@ async def test_remove_older_logs(hass, simple_queue, hass_client):
|
||||
_LOGGER.error("error message 3")
|
||||
await _async_block_until_queue_empty(hass, simple_queue)
|
||||
|
||||
log = await get_error_log(hass, hass_client, 2)
|
||||
log = await get_error_log(hass_ws_client)
|
||||
assert_log(log[0], "", "error message 3", "ERROR")
|
||||
assert_log(log[1], "", "error message 2", "ERROR")
|
||||
|
||||
@ -182,7 +189,7 @@ def log_msg(nr=2):
|
||||
_LOGGER.error("error message %s", nr)
|
||||
|
||||
|
||||
async def test_dedupe_logs(hass, simple_queue, hass_client):
|
||||
async def test_dedupe_logs(hass, simple_queue, hass_ws_client):
|
||||
"""Test that duplicate log entries are dedupe."""
|
||||
await async_setup_component(hass, system_log.DOMAIN, {})
|
||||
_LOGGER.error("error message 1")
|
||||
@ -191,7 +198,7 @@ async def test_dedupe_logs(hass, simple_queue, hass_client):
|
||||
_LOGGER.error("error message 3")
|
||||
await _async_block_until_queue_empty(hass, simple_queue)
|
||||
|
||||
log = await get_error_log(hass, hass_client, 3)
|
||||
log = await get_error_log(hass_ws_client)
|
||||
assert_log(log[0], "", "error message 3", "ERROR")
|
||||
assert log[1]["count"] == 2
|
||||
assert_log(log[1], "", ["error message 2", "error message 2-2"], "ERROR")
|
||||
@ -199,7 +206,7 @@ async def test_dedupe_logs(hass, simple_queue, hass_client):
|
||||
log_msg()
|
||||
await _async_block_until_queue_empty(hass, simple_queue)
|
||||
|
||||
log = await get_error_log(hass, hass_client, 3)
|
||||
log = await get_error_log(hass_ws_client)
|
||||
assert_log(log[0], "", ["error message 2", "error message 2-2"], "ERROR")
|
||||
assert log[0]["timestamp"] > log[0]["first_occurred"]
|
||||
|
||||
@ -209,7 +216,7 @@ async def test_dedupe_logs(hass, simple_queue, hass_client):
|
||||
log_msg("2-6")
|
||||
await _async_block_until_queue_empty(hass, simple_queue)
|
||||
|
||||
log = await get_error_log(hass, hass_client, 3)
|
||||
log = await get_error_log(hass_ws_client)
|
||||
assert_log(
|
||||
log[0],
|
||||
"",
|
||||
@ -224,7 +231,7 @@ async def test_dedupe_logs(hass, simple_queue, hass_client):
|
||||
)
|
||||
|
||||
|
||||
async def test_clear_logs(hass, simple_queue, hass_client):
|
||||
async def test_clear_logs(hass, simple_queue, hass_ws_client):
|
||||
"""Test that the log can be cleared via a service call."""
|
||||
await async_setup_component(hass, system_log.DOMAIN, BASIC_CONFIG)
|
||||
_LOGGER.error("error message")
|
||||
@ -234,7 +241,7 @@ async def test_clear_logs(hass, simple_queue, hass_client):
|
||||
await _async_block_until_queue_empty(hass, simple_queue)
|
||||
|
||||
# Assert done by get_error_log
|
||||
await get_error_log(hass, hass_client, 0)
|
||||
await get_error_log(hass_ws_client)
|
||||
|
||||
|
||||
async def test_write_log(hass):
|
||||
@ -277,13 +284,13 @@ async def test_write_choose_level(hass):
|
||||
assert logger.method_calls[0] == ("debug", ("test_message",))
|
||||
|
||||
|
||||
async def test_unknown_path(hass, simple_queue, hass_client):
|
||||
async def test_unknown_path(hass, simple_queue, hass_ws_client):
|
||||
"""Test error logged from unknown path."""
|
||||
await async_setup_component(hass, system_log.DOMAIN, BASIC_CONFIG)
|
||||
_LOGGER.findCaller = MagicMock(return_value=("unknown_path", 0, None, None))
|
||||
_LOGGER.error("error message")
|
||||
await _async_block_until_queue_empty(hass, simple_queue)
|
||||
log = (await get_error_log(hass, hass_client, 1))[0]
|
||||
log = (await get_error_log(hass_ws_client))[0]
|
||||
assert log["source"] == ["unknown_path", 0]
|
||||
|
||||
|
||||
@ -307,7 +314,7 @@ async def async_log_error_from_test_path(hass, path, sq):
|
||||
await _async_block_until_queue_empty(hass, sq)
|
||||
|
||||
|
||||
async def test_homeassistant_path(hass, simple_queue, hass_client):
|
||||
async def test_homeassistant_path(hass, simple_queue, hass_ws_client):
|
||||
"""Test error logged from Home Assistant path."""
|
||||
await async_setup_component(hass, system_log.DOMAIN, BASIC_CONFIG)
|
||||
with patch(
|
||||
@ -317,16 +324,16 @@ async def test_homeassistant_path(hass, simple_queue, hass_client):
|
||||
await async_log_error_from_test_path(
|
||||
hass, "venv_path/homeassistant/component/component.py", simple_queue
|
||||
)
|
||||
log = (await get_error_log(hass, hass_client, 1))[0]
|
||||
log = (await get_error_log(hass_ws_client))[0]
|
||||
assert log["source"] == ["component/component.py", 5]
|
||||
|
||||
|
||||
async def test_config_path(hass, simple_queue, hass_client):
|
||||
async def test_config_path(hass, simple_queue, hass_ws_client):
|
||||
"""Test error logged from config path."""
|
||||
await async_setup_component(hass, system_log.DOMAIN, BASIC_CONFIG)
|
||||
with patch.object(hass.config, "config_dir", new="config"):
|
||||
await async_log_error_from_test_path(
|
||||
hass, "config/custom_component/test.py", simple_queue
|
||||
)
|
||||
log = (await get_error_log(hass, hass_client, 1))[0]
|
||||
log = (await get_error_log(hass_ws_client))[0]
|
||||
assert log["source"] == ["custom_component/test.py", 5]
|
||||
|
Loading…
x
Reference in New Issue
Block a user