From e2b817f785f2132f1ab13301cd73d497218500ae Mon Sep 17 00:00:00 2001 From: zosimovaa Date: Mon, 4 May 2026 13:17:22 +0300 Subject: [PATCH] =?UTF-8?q?INF-3=20=D0=9F=D0=BE=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=B8=D0=BB=20=D1=88=D0=B0=D0=BF=D0=BA=D1=83=20=D1=81=20=D1=82?= =?UTF-8?q?=D1=80=D0=B5=D0=B9=D1=81=20=D0=B8=D0=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyproject.toml | 2 +- src/app_runtime/control/trace_presenter.py | 43 ++++++++++++----- tests/test_trace_endpoint.py | 55 +++++++++++++--------- 3 files changed, 65 insertions(+), 35 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index dd38676..af0457d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "plba" -version = "0.3.19" +version = "0.3.20" description = "Platform runtime for business applications" readme = "README.md" requires-python = ">=3.11" diff --git a/src/app_runtime/control/trace_presenter.py b/src/app_runtime/control/trace_presenter.py index e0432b5..2df7236 100644 --- a/src/app_runtime/control/trace_presenter.py +++ b/src/app_runtime/control/trace_presenter.py @@ -11,6 +11,9 @@ from app_runtime.control.base import TraceQueryRequest from app_runtime.contracts.trace import TraceLevel, TraceLogRecord, TraceLogView +TRACE_SECTION_SEPARATOR = "=" * 30 + + class TraceRequestParser: def parse(self, request: Request) -> TraceQueryRequest: raw_levels = request.query_params.get("levels") @@ -85,11 +88,13 @@ class TraceResponseRenderer: def _render_text(self, trace_view: TraceLogView, request: TraceQueryRequest) -> PlainTextResponse: lineage = [*trace_view.ancestors, trace_view] - lines: list[str] = [] + lines = self._text_trace_summary_lines(trace_view) for index, entry in enumerate(lineage): - if index > 0: + if index == 0: lines.append("") - lines.extend(self._text_trace_lines(entry, request)) + else: + lines.extend(["", ""]) + lines.extend(self._text_trace_log_lines(entry, request)) return PlainTextResponse(content="\n".join(lines)) def _render_html(self, trace_view: TraceLogView, request: TraceQueryRequest) -> HTMLResponse: @@ -183,11 +188,13 @@ class TraceResponseRenderer: def _html_lines(self, trace_view: TraceLogView, request: TraceQueryRequest) -> str: lineage = [*trace_view.ancestors, trace_view] - lines: list[str] = [] + lines = self._html_trace_summary_lines(trace_view, request) for index, entry in enumerate(lineage): - if index > 0: + if index == 0: lines.append(self._html_plain_line("")) - lines.extend(self._html_trace_lines(entry, request)) + else: + lines.extend([self._html_plain_line(""), self._html_plain_line("")]) + lines.extend(self._html_trace_log_lines(entry, request)) return "".join(lines) def _trace_payload(self, trace_view: TraceLogView, request: TraceQueryRequest) -> dict[str, object]: @@ -198,12 +205,18 @@ class TraceResponseRenderer: "messages": [record.as_dict(include_attrs_json=request.include_attrs_json) for record in trace_view.records], } - def _text_trace_lines(self, trace_view: TraceLogView, request: TraceQueryRequest) -> list[str]: - lines = [ + def _text_trace_summary_lines(self, trace_view: TraceLogView) -> list[str]: + return [ f"trace_id: {trace_view.trace_id}", f"parent_id: {trace_view.parent_id or ''}", *self._child_id_lines(trace_view.child_ids), - "------------------------------", + ] + + def _text_trace_log_lines(self, trace_view: TraceLogView, request: TraceQueryRequest) -> list[str]: + lines = [ + TRACE_SECTION_SEPARATOR, + f"trace_id: {trace_view.trace_id}", + "", ] previous_step: str | None = None for record in trace_view.records: @@ -217,13 +230,19 @@ class TraceResponseRenderer: lines.append(self._text_message(record, request.include_attrs_json)) return lines - def _html_trace_lines(self, trace_view: TraceLogView, request: TraceQueryRequest) -> list[str]: - lines = [ + def _html_trace_summary_lines(self, trace_view: TraceLogView, request: TraceQueryRequest) -> list[str]: + return [ self._html_plain_line(f"trace_id: {self._trace_link(trace_view.trace_id, request)}"), self._html_plain_line(f"parent_id: {self._optional_trace_link(trace_view.parent_id, request)}"), self._html_plain_line("child_ids:"), *(self._html_plain_line(f" - {self._trace_link(child_id, request)}") for child_id in trace_view.child_ids), - self._html_plain_line("------------------------------"), + ] + + def _html_trace_log_lines(self, trace_view: TraceLogView, request: TraceQueryRequest) -> list[str]: + lines = [ + self._html_plain_line(TRACE_SECTION_SEPARATOR), + self._html_plain_line(f"trace_id: {self._trace_link(trace_view.trace_id, request)}"), + self._html_plain_line(""), ] previous_step: str | None = None for record in trace_view.records: diff --git a/tests/test_trace_endpoint.py b/tests/test_trace_endpoint.py index bf27fea..4e48509 100644 --- a/tests/test_trace_endpoint.py +++ b/tests/test_trace_endpoint.py @@ -110,7 +110,10 @@ def test_trace_endpoint_returns_text_when_requested() -> None: "child_ids:\n" " - child-1\n" " - child-2\n" - "------------------------------\n" + "\n" + "==============================\n" + "trace_id: trace-1\n" + "\n" "step: process\n" "first error\n" "second warning" @@ -139,7 +142,10 @@ def test_trace_endpoint_appends_attrs_json_in_text_mode() -> None: "trace_id: trace-1\n" "parent_id: \n" "child_ids:\n" - "------------------------------\n" + "\n" + "==============================\n" + "trace_id: trace-1\n" + "\n" "step: process\n" 'failure, {"attempt":2,"source":"crm"}' ) @@ -169,7 +175,10 @@ def test_trace_endpoint_separates_messages_by_step_in_text_mode() -> None: "trace_id: trace-1\n" "parent_id: \n" "child_ids:\n" - "------------------------------\n" + "\n" + "==============================\n" + "trace_id: trace-1\n" + "\n" "step: load_stocks\n" "load first\n" "load second\n" @@ -323,8 +332,9 @@ def test_trace_endpoint_returns_html_page_with_related_links() -> None: assert '
child_ids:
' in response.text assert '
- child-1
' in response.text assert '
- child-2
' in response.text + assert '
==============================
' in response.text assert '
load_stocks
' in response.text - assert '
------------------------------
' in response.text + assert '
trace_id: trace-1
' in response.text assert '
filter_stocks
' in response.text assert "loaded prices" in response.text assert "filtered suspicious ticker" in response.text @@ -364,26 +374,27 @@ def test_trace_endpoint_renders_ancestors_in_text_mode() -> None: assert response.status_code == 200 assert response.text == ( - "trace_id: root-1\n" - "parent_id: \n" - "child_ids:\n" - " - parent-1\n" - "------------------------------\n" - "step: process\n" - "root message\n" - "\n" - "trace_id: parent-1\n" - "parent_id: root-1\n" - "child_ids:\n" - " - trace-1\n" - "------------------------------\n" - "step: process\n" - "parent message\n" - "\n" "trace_id: trace-1\n" "parent_id: parent-1\n" "child_ids:\n" - "------------------------------\n" + "\n" + "==============================\n" + "trace_id: root-1\n" + "\n" + "step: process\n" + "root message\n" + "\n" + "\n" + "==============================\n" + "trace_id: parent-1\n" + "\n" + "step: process\n" + "parent message\n" + "\n" + "\n" + "==============================\n" + "trace_id: trace-1\n" + "\n" "step: process\n" "child message" ) @@ -419,10 +430,10 @@ def test_trace_endpoint_preserves_ancestor_depth_in_html_links() -> None: client.close() assert response.status_code == 200 - assert response.text.index('href="/traces/root-1?format=html&levels=ERROR%2CWARNING%2CINFO&attrs_json=true&ancestor_depth=all"') < response.text.index('href="/traces/parent-1?format=html&levels=ERROR%2CWARNING%2CINFO&attrs_json=true&ancestor_depth=all"') < response.text.index('href="/traces/trace-1?format=html&levels=ERROR%2CWARNING%2CINFO&attrs_json=true&ancestor_depth=all"') assert 'href="/traces/trace-1?format=html&levels=ERROR%2CWARNING%2CINFO&attrs_json=true&ancestor_depth=all"' in response.text assert 'href="/traces/root-1?format=html&levels=ERROR%2CWARNING%2CINFO&attrs_json=true&ancestor_depth=all"' in response.text assert 'href="/traces/parent-1?format=html&levels=ERROR%2CWARNING%2CINFO&attrs_json=true&ancestor_depth=all"' in response.text + assert response.text.index("root info") < response.text.index("parent warning") < response.text.index("loaded prices") assert "root info" in response.text assert "parent warning" in response.text