INF-3 Поправил шапку с трейс ид

This commit is contained in:
2026-05-04 13:17:22 +03:00
parent 8cad1d00ec
commit e2b817f785
3 changed files with 65 additions and 35 deletions
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "plba" name = "plba"
version = "0.3.19" version = "0.3.20"
description = "Platform runtime for business applications" description = "Platform runtime for business applications"
readme = "README.md" readme = "README.md"
requires-python = ">=3.11" requires-python = ">=3.11"
+31 -12
View File
@@ -11,6 +11,9 @@ from app_runtime.control.base import TraceQueryRequest
from app_runtime.contracts.trace import TraceLevel, TraceLogRecord, TraceLogView from app_runtime.contracts.trace import TraceLevel, TraceLogRecord, TraceLogView
TRACE_SECTION_SEPARATOR = "=" * 30
class TraceRequestParser: class TraceRequestParser:
def parse(self, request: Request) -> TraceQueryRequest: def parse(self, request: Request) -> TraceQueryRequest:
raw_levels = request.query_params.get("levels") raw_levels = request.query_params.get("levels")
@@ -85,11 +88,13 @@ class TraceResponseRenderer:
def _render_text(self, trace_view: TraceLogView, request: TraceQueryRequest) -> PlainTextResponse: def _render_text(self, trace_view: TraceLogView, request: TraceQueryRequest) -> PlainTextResponse:
lineage = [*trace_view.ancestors, trace_view] lineage = [*trace_view.ancestors, trace_view]
lines: list[str] = [] lines = self._text_trace_summary_lines(trace_view)
for index, entry in enumerate(lineage): for index, entry in enumerate(lineage):
if index > 0: if index == 0:
lines.append("") 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)) return PlainTextResponse(content="\n".join(lines))
def _render_html(self, trace_view: TraceLogView, request: TraceQueryRequest) -> HTMLResponse: 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: def _html_lines(self, trace_view: TraceLogView, request: TraceQueryRequest) -> str:
lineage = [*trace_view.ancestors, trace_view] lineage = [*trace_view.ancestors, trace_view]
lines: list[str] = [] lines = self._html_trace_summary_lines(trace_view, request)
for index, entry in enumerate(lineage): for index, entry in enumerate(lineage):
if index > 0: if index == 0:
lines.append(self._html_plain_line("")) 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) return "".join(lines)
def _trace_payload(self, trace_view: TraceLogView, request: TraceQueryRequest) -> dict[str, object]: 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], "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]: def _text_trace_summary_lines(self, trace_view: TraceLogView) -> list[str]:
lines = [ return [
f"trace_id: {trace_view.trace_id}", f"trace_id: {trace_view.trace_id}",
f"parent_id: {trace_view.parent_id or ''}", f"parent_id: {trace_view.parent_id or ''}",
*self._child_id_lines(trace_view.child_ids), *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 previous_step: str | None = None
for record in trace_view.records: for record in trace_view.records:
@@ -217,13 +230,19 @@ class TraceResponseRenderer:
lines.append(self._text_message(record, request.include_attrs_json)) lines.append(self._text_message(record, request.include_attrs_json))
return lines return lines
def _html_trace_lines(self, trace_view: TraceLogView, request: TraceQueryRequest) -> list[str]: def _html_trace_summary_lines(self, trace_view: TraceLogView, request: TraceQueryRequest) -> list[str]:
lines = [ return [
self._html_plain_line(f"trace_id: {self._trace_link(trace_view.trace_id, request)}"), 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(f"parent_id: {self._optional_trace_link(trace_view.parent_id, request)}"),
self._html_plain_line("child_ids:"), 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(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 previous_step: str | None = None
for record in trace_view.records: for record in trace_view.records:
+33 -22
View File
@@ -110,7 +110,10 @@ def test_trace_endpoint_returns_text_when_requested() -> None:
"child_ids:\n" "child_ids:\n"
" - child-1\n" " - child-1\n"
" - child-2\n" " - child-2\n"
"------------------------------\n" "\n"
"==============================\n"
"trace_id: trace-1\n"
"\n"
"step: process\n" "step: process\n"
"first error\n" "first error\n"
"second warning" "second warning"
@@ -139,7 +142,10 @@ def test_trace_endpoint_appends_attrs_json_in_text_mode() -> None:
"trace_id: trace-1\n" "trace_id: trace-1\n"
"parent_id: \n" "parent_id: \n"
"child_ids:\n" "child_ids:\n"
"------------------------------\n" "\n"
"==============================\n"
"trace_id: trace-1\n"
"\n"
"step: process\n" "step: process\n"
'failure, {"attempt":2,"source":"crm"}' '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" "trace_id: trace-1\n"
"parent_id: \n" "parent_id: \n"
"child_ids:\n" "child_ids:\n"
"------------------------------\n" "\n"
"==============================\n"
"trace_id: trace-1\n"
"\n"
"step: load_stocks\n" "step: load_stocks\n"
"load first\n" "load first\n"
"load second\n" "load second\n"
@@ -323,8 +332,9 @@ def test_trace_endpoint_returns_html_page_with_related_links() -> None:
assert '<div class="line">child_ids:</div>' in response.text assert '<div class="line">child_ids:</div>' in response.text
assert '<div class="line"> - <a href="/traces/child-1?format=html&amp;levels=ERROR%2CWARNING%2CINFO&amp;attrs_json=true">child-1</a></div>' in response.text assert '<div class="line"> - <a href="/traces/child-1?format=html&amp;levels=ERROR%2CWARNING%2CINFO&amp;attrs_json=true">child-1</a></div>' in response.text
assert '<div class="line"> - <a href="/traces/child-2?format=html&amp;levels=ERROR%2CWARNING%2CINFO&amp;attrs_json=true">child-2</a></div>' in response.text assert '<div class="line"> - <a href="/traces/child-2?format=html&amp;levels=ERROR%2CWARNING%2CINFO&amp;attrs_json=true">child-2</a></div>' in response.text
assert '<div class="line">==============================</div>' in response.text
assert '<div class="line" style="color: var(--step);">load_stocks</div>' in response.text assert '<div class="line" style="color: var(--step);">load_stocks</div>' in response.text
assert '<div class="line">------------------------------</div>' in response.text assert '<div class="line">trace_id: <a href="/traces/trace-1?format=html&amp;levels=ERROR%2CWARNING%2CINFO&amp;attrs_json=true">trace-1</a></div>' in response.text
assert '<div class="line" style="color: var(--step);">filter_stocks</div>' in response.text assert '<div class="line" style="color: var(--step);">filter_stocks</div>' in response.text
assert "loaded prices" in response.text assert "loaded prices" in response.text
assert "filtered suspicious ticker" 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.status_code == 200
assert response.text == ( 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" "trace_id: trace-1\n"
"parent_id: parent-1\n" "parent_id: parent-1\n"
"child_ids:\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" "step: process\n"
"child message" "child message"
) )
@@ -419,10 +430,10 @@ def test_trace_endpoint_preserves_ancestor_depth_in_html_links() -> None:
client.close() client.close()
assert response.status_code == 200 assert response.status_code == 200
assert response.text.index('href="/traces/root-1?format=html&amp;levels=ERROR%2CWARNING%2CINFO&amp;attrs_json=true&amp;ancestor_depth=all"') < response.text.index('href="/traces/parent-1?format=html&amp;levels=ERROR%2CWARNING%2CINFO&amp;attrs_json=true&amp;ancestor_depth=all"') < response.text.index('href="/traces/trace-1?format=html&amp;levels=ERROR%2CWARNING%2CINFO&amp;attrs_json=true&amp;ancestor_depth=all"')
assert 'href="/traces/trace-1?format=html&amp;levels=ERROR%2CWARNING%2CINFO&amp;attrs_json=true&amp;ancestor_depth=all"' in response.text assert 'href="/traces/trace-1?format=html&amp;levels=ERROR%2CWARNING%2CINFO&amp;attrs_json=true&amp;ancestor_depth=all"' in response.text
assert 'href="/traces/root-1?format=html&amp;levels=ERROR%2CWARNING%2CINFO&amp;attrs_json=true&amp;ancestor_depth=all"' in response.text assert 'href="/traces/root-1?format=html&amp;levels=ERROR%2CWARNING%2CINFO&amp;attrs_json=true&amp;ancestor_depth=all"' in response.text
assert 'href="/traces/parent-1?format=html&amp;levels=ERROR%2CWARNING%2CINFO&amp;attrs_json=true&amp;ancestor_depth=all"' in response.text assert 'href="/traces/parent-1?format=html&amp;levels=ERROR%2CWARNING%2CINFO&amp;attrs_json=true&amp;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 "root info" in response.text
assert "parent warning" in response.text assert "parent warning" in response.text