diff --git a/pyproject.toml b/pyproject.toml index 9132841..dd38676 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "plba" -version = "0.3.18" +version = "0.3.19" 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 239a9c2..e0432b5 100644 --- a/src/app_runtime/control/trace_presenter.py +++ b/src/app_runtime/control/trace_presenter.py @@ -84,9 +84,12 @@ class TraceResponseRenderer: ) def _render_text(self, trace_view: TraceLogView, request: TraceQueryRequest) -> PlainTextResponse: - lines = self._text_trace_lines(trace_view, request) - for index, ancestor in enumerate(trace_view.ancestors, start=1): - lines.extend(["", f"ancestor[{index}]:", *self._text_trace_lines(ancestor, request)]) + lineage = [*trace_view.ancestors, trace_view] + lines: list[str] = [] + for index, entry in enumerate(lineage): + if index > 0: + lines.append("") + lines.extend(self._text_trace_lines(entry, request)) return PlainTextResponse(content="\n".join(lines)) def _render_html(self, trace_view: TraceLogView, request: TraceQueryRequest) -> HTMLResponse: @@ -179,15 +182,12 @@ class TraceResponseRenderer: return f"/traces/{trace_id}?{query}" def _html_lines(self, trace_view: TraceLogView, request: TraceQueryRequest) -> str: - lines = self._html_trace_lines(trace_view, request) - for index, ancestor in enumerate(trace_view.ancestors, start=1): - lines.extend( - [ - self._html_plain_line(""), - self._html_plain_line(f"ancestor[{index}]:"), - *self._html_trace_lines(ancestor, request), - ] - ) + lineage = [*trace_view.ancestors, trace_view] + lines: list[str] = [] + for index, entry in enumerate(lineage): + if index > 0: + lines.append(self._html_plain_line("")) + lines.extend(self._html_trace_lines(entry, request)) return "".join(lines) def _trace_payload(self, trace_view: TraceLogView, request: TraceQueryRequest) -> dict[str, object]: diff --git a/src/app_runtime/tracing/reader.py b/src/app_runtime/tracing/reader.py index 2f90fea..e6ba943 100644 --- a/src/app_runtime/tracing/reader.py +++ b/src/app_runtime/tracing/reader.py @@ -55,6 +55,7 @@ class MySqlTraceLogReader(TraceLogReader): current_trace_id = current_parent_id if remaining_depth is not None: remaining_depth -= 1 + ancestors.reverse() return ancestors def _trace_exists(self, trace_id: str) -> bool: diff --git a/tests/test_trace_endpoint.py b/tests/test_trace_endpoint.py index c0cf531..bf27fea 100644 --- a/tests/test_trace_endpoint.py +++ b/tests/test_trace_endpoint.py @@ -227,12 +227,20 @@ def test_trace_endpoint_returns_json_payload_with_ancestors() -> None: _trace_record(row_id=3, level="INFO", message="done", attrs_json={"batch": 7}), ), ancestors=( + TraceLogView( + trace_id="root-1", + parent_id=None, + child_ids=("parent-1",), + records=( + _trace_record(row_id=4, level="INFO", message="root info"), + ), + ), TraceLogView( trace_id="parent-1", parent_id="root-1", child_ids=("trace-1", "sibling-1"), records=( - _trace_record(row_id=4, level="WARNING", message="parent warning"), + _trace_record(row_id=5, level="WARNING", message="parent warning"), ), ), ), @@ -246,13 +254,29 @@ def test_trace_endpoint_returns_json_payload_with_ancestors() -> None: assert response.status_code == 200 assert response.json()["ancestors"] == [ + { + "trace_id": "root-1", + "parent_id": "", + "child_ids": ["parent-1"], + "messages": [ + { + "id": 4, + "trace_id": "trace-1", + "event_time": "2026-04-28T10:11:12+00:00", + "step": "process", + "status": "failed", + "level": "INFO", + "message": "root info", + } + ], + }, { "trace_id": "parent-1", "parent_id": "root-1", "child_ids": ["trace-1", "sibling-1"], "messages": [ { - "id": 4, + "id": 5, "trace_id": "trace-1", "event_time": "2026-04-28T10:11:12+00:00", "step": "process", @@ -317,11 +341,17 @@ def test_trace_endpoint_renders_ancestors_in_text_mode() -> None: child_ids=(), records=(_trace_record(row_id=1, level="INFO", message="child message"),), ancestors=( + TraceLogView( + trace_id="root-1", + parent_id=None, + child_ids=("parent-1",), + records=(_trace_record(row_id=2, level="INFO", message="root message"),), + ), TraceLogView( trace_id="parent-1", parent_id="root-1", child_ids=("trace-1",), - records=(_trace_record(row_id=2, level="WARNING", message="parent message"),), + records=(_trace_record(row_id=3, level="WARNING", message="parent message"),), ), ), ) @@ -334,21 +364,28 @@ def test_trace_endpoint_renders_ancestors_in_text_mode() -> None: assert response.status_code == 200 assert response.text == ( - "trace_id: trace-1\n" - "parent_id: parent-1\n" + "trace_id: root-1\n" + "parent_id: \n" "child_ids:\n" + " - parent-1\n" "------------------------------\n" "step: process\n" - "child message\n" + "root message\n" "\n" - "ancestor[1]:\n" "trace_id: parent-1\n" "parent_id: root-1\n" "child_ids:\n" " - trace-1\n" "------------------------------\n" "step: process\n" - "parent message" + "parent message\n" + "\n" + "trace_id: trace-1\n" + "parent_id: parent-1\n" + "child_ids:\n" + "------------------------------\n" + "step: process\n" + "child message" ) @@ -360,11 +397,17 @@ def test_trace_endpoint_preserves_ancestor_depth_in_html_links() -> None: child_ids=("child-1",), records=(_trace_record(row_id=1, level="INFO", message="loaded prices"),), ancestors=( + TraceLogView( + trace_id="root-1", + parent_id=None, + child_ids=("parent-1",), + records=(_trace_record(row_id=2, level="INFO", message="root info"),), + ), TraceLogView( trace_id="parent-1", parent_id="root-1", child_ids=("trace-1",), - records=(_trace_record(row_id=2, level="WARNING", message="parent warning"),), + records=(_trace_record(row_id=3, level="WARNING", message="parent warning"),), ), ), ) @@ -376,9 +419,11 @@ 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 '