From 05e0c7c3ab949ec26c98f5dc98c4d8cf52ee3c0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cerm=C3=A1k?= Date: Wed, 4 Sep 2024 16:11:37 +0200 Subject: [PATCH] Add "lines" and "verbose" query parameters for advanced logs (#5287) Since headers are clumsy considering the Core proxy between the frontend and Supervisor, add a way to adjust number of lines and verbose log format using query parameters as well. If both query parameters and headers are supplied, prefer the former, as it's more prominent when reading through the request logs. --- supervisor/api/host.py | 8 ++++++-- tests/api/test_host.py | 45 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/supervisor/api/host.py b/supervisor/api/host.py index df54f77d8c5..50bfa9190ce 100644 --- a/supervisor/api/host.py +++ b/supervisor/api/host.py @@ -222,10 +222,14 @@ async def advanced_logs_handler( "supported for now." ) - if request.headers[ACCEPT] == CONTENT_TYPE_X_LOG: + if "verbose" in request.query or request.headers[ACCEPT] == CONTENT_TYPE_X_LOG: log_formatter = LogFormatter.VERBOSE - if RANGE in request.headers: + if "lines" in request.query: + lines = request.query.get("lines", DEFAULT_RANGE) + # entries=cursor[[:num_skip]:num_entries] + range_header = f"entries=:-{lines}:" + elif RANGE in request.headers: range_header = request.headers.get(RANGE) else: range_header = f"entries=:-{DEFAULT_RANGE}:" diff --git a/tests/api/test_host.py b/tests/api/test_host.py index 41815f22a37..9c5d2901677 100644 --- a/tests/api/test_host.py +++ b/tests/api/test_host.py @@ -238,6 +238,51 @@ async def test_advanced_logs( ) +async def test_advaced_logs_query_parameters( + api_client: TestClient, + coresys: CoreSys, + journald_logs: MagicMock, + journal_logs_reader: MagicMock, +): + """Test advanced logging API entries controlled by query parameters.""" + # Check lines query parameter + await api_client.get("/host/logs?lines=53") + journald_logs.assert_called_once_with( + params={"SYSLOG_IDENTIFIER": coresys.host.logs.default_identifiers}, + range_header="entries=:-53:", + accept=LogFormat.JOURNAL, + ) + + journald_logs.reset_mock() + + # Check verbose logs formatter via query parameter + await api_client.get("/host/logs?verbose") + journald_logs.assert_called_once_with( + params={"SYSLOG_IDENTIFIER": coresys.host.logs.default_identifiers}, + range_header=DEFAULT_RANGE, + accept=LogFormat.JOURNAL, + ) + journal_logs_reader.assert_called_with(ANY, LogFormatter.VERBOSE) + + journal_logs_reader.reset_mock() + journald_logs.reset_mock() + + # Query parameters should take precedence over headers + await api_client.get( + "/host/logs?lines=53&verbose", + headers={ + "Range": "entries=:-19:10", + "Accept": "text/plain", + }, + ) + journald_logs.assert_called_once_with( + params={"SYSLOG_IDENTIFIER": coresys.host.logs.default_identifiers}, + range_header="entries=:-53:", + accept=LogFormat.JOURNAL, + ) + journal_logs_reader.assert_called_with(ANY, LogFormatter.VERBOSE) + + async def test_advanced_logs_boot_id_offset( api_client: TestClient, coresys: CoreSys, journald_logs: MagicMock ):