diff --git a/pyproject.toml b/pyproject.toml index cace5e4..0660b5a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "plba" -version = "0.5.0" +version = "0.5.1" description = "Platform runtime for business applications" readme = "README.md" requires-python = ">=3.11" diff --git a/src/app_runtime/contracts/trace.py b/src/app_runtime/contracts/trace.py index a074d9d..09a1bf3 100644 --- a/src/app_runtime/contracts/trace.py +++ b/src/app_runtime/contracts/trace.py @@ -105,6 +105,6 @@ class TraceLogReader(Protocol): levels: tuple[TraceLevel, ...], ancestor_depth: int | None = 0, *, - view: Literal["tree", "single"] = "tree", + view: Literal["tree", "single"] = "single", ) -> TraceLogView | None: """Load trace context and filtered log records.""" diff --git a/src/app_runtime/control/base.py b/src/app_runtime/control/base.py index 8deefb3..b41c985 100644 --- a/src/app_runtime/control/base.py +++ b/src/app_runtime/control/base.py @@ -29,7 +29,7 @@ class TraceQueryRequest: include_attrs_json: bool = False response_format: TraceResponseFormat = "html" ancestor_depth: int | None = None - view: TraceViewMode = "tree" + view: TraceViewMode = "single" TraceLookupHandler = Callable[[str, TraceQueryRequest], Awaitable[TraceLogView]] diff --git a/src/app_runtime/control/trace_presenter.py b/src/app_runtime/control/trace_presenter.py index 6d12056..6932919 100644 --- a/src/app_runtime/control/trace_presenter.py +++ b/src/app_runtime/control/trace_presenter.py @@ -21,7 +21,7 @@ class TraceRequestParser: response_format = raw_format.strip().lower() if response_format not in {"json", "text", "html"}: raise ValueError(f"unsupported trace format: {raw_format}") - raw_view = request.query_params.get("view", "tree") + raw_view = request.query_params.get("view", "single") view = raw_view.strip().lower() if view not in {"tree", "single"}: raise ValueError(f"unsupported trace view: {raw_view}") diff --git a/tests/artifacts/trace_logs/tests-test_app.py-test_email_processing_empty_inbox_records_no_new_emails_message-1.html b/tests/artifacts/trace_logs/tests-test_app.py-test_email_processing_empty_inbox_records_no_new_emails_message-1.html new file mode 100644 index 0000000..10c3b99 --- /dev/null +++ b/tests/artifacts/trace_logs/tests-test_app.py-test_email_processing_empty_inbox_records_no_new_emails_message-1.html @@ -0,0 +1,61 @@ + + + + + + Trace 3afa2bd3-db74-433b-b852-4be5bdf2c60b + + + +
+
trace_id: 3afa2bd3-db74-433b-b852-4be5bdf2c60b
parent_id:
child_ids:
 
==============================
trace context: , 3afa2bd3-db74-433b-b852-4be5bdf2c60b
 
email-processing
 
Новых писем не найдено., {}
+
+ + \ No newline at end of file diff --git a/tests/artifacts/trace_logs/tests-test_app.py-test_email_processing_empty_inbox_records_no_new_emails_message-2.html b/tests/artifacts/trace_logs/tests-test_app.py-test_email_processing_empty_inbox_records_no_new_emails_message-2.html new file mode 100644 index 0000000..10c3b99 --- /dev/null +++ b/tests/artifacts/trace_logs/tests-test_app.py-test_email_processing_empty_inbox_records_no_new_emails_message-2.html @@ -0,0 +1,61 @@ + + + + + + Trace 3afa2bd3-db74-433b-b852-4be5bdf2c60b + + + +
+
trace_id: 3afa2bd3-db74-433b-b852-4be5bdf2c60b
parent_id:
child_ids:
 
==============================
trace context: , 3afa2bd3-db74-433b-b852-4be5bdf2c60b
 
email-processing
 
Новых писем не найдено., {}
+
+ + \ No newline at end of file diff --git a/tests/artifacts/trace_logs/tests-test_app.py-test_health_endpoint_exposes_components-1.html b/tests/artifacts/trace_logs/tests-test_app.py-test_health_endpoint_exposes_components-1.html new file mode 100644 index 0000000..a46dbd3 --- /dev/null +++ b/tests/artifacts/trace_logs/tests-test_app.py-test_health_endpoint_exposes_components-1.html @@ -0,0 +1,61 @@ + + + + + + Trace dc5769f8-4451-4acc-a923-0de56283285e + + + +
+
trace_id: dc5769f8-4451-4acc-a923-0de56283285e
parent_id:
child_ids:
 
==============================
trace context: , dc5769f8-4451-4acc-a923-0de56283285e
 
order-sync
 
Подготовлен запрос синхронизации заказов ABCP. period_start=2026-08-11T20:10:04.926255+00:00, period_end=2026-08-12T20:10:04.926255+00:00., {"period_start":"2026-08-11T20:10:04.926255+00:00","period_end":"2026-08-12T20:10:04.926255+00:00","abcp_request_url":"static://cp/orders"}
Подготовлен запрос синхронизации заказов ABCP. period_start=2026-08-12T20:10:04.924987+00:00, period_end=2026-08-12T20:10:04.926255+00:00., {"period_start":"2026-08-12T20:10:04.924987+00:00","period_end":"2026-08-12T20:10:04.926255+00:00","abcp_request_url":"static://cp/orders"}
Синхронизация заказов завершена. records_loaded=1, period_start=2026-08-11T20:10:04.926255+00:00, period_end=2026-08-12T20:10:04.926255+00:00., {"records_loaded":1,"period_start":"2026-08-11T20:10:04.926255+00:00","period_end":"2026-08-12T20:10:04.926255+00:00"}
+
+ + \ No newline at end of file diff --git a/tests/artifacts/trace_logs/tests-test_app.py-test_order_sync_loads_multiple_batches_until_sync_window_is_closed-1.html b/tests/artifacts/trace_logs/tests-test_app.py-test_order_sync_loads_multiple_batches_until_sync_window_is_closed-1.html new file mode 100644 index 0000000..f1cc153 --- /dev/null +++ b/tests/artifacts/trace_logs/tests-test_app.py-test_order_sync_loads_multiple_batches_until_sync_window_is_closed-1.html @@ -0,0 +1,61 @@ + + + + + + Trace c877db56-2148-47d0-9cb8-c8366ecb094a + + + +
+
trace_id: c877db56-2148-47d0-9cb8-c8366ecb094a
parent_id:
child_ids:
 
==============================
trace context: , c877db56-2148-47d0-9cb8-c8366ecb094a
 
order-sync
 
Подготовлен запрос синхронизации заказов ABCP. period_start=2026-04-14T20:10:04.612913+00:00, period_end=2026-08-12T20:10:04.612913+00:00., {"period_start":"2026-04-14T20:10:04.612913+00:00","period_end":"2026-08-12T20:10:04.612913+00:00","abcp_request_url":"unknown://cp/orders"}
Подготовлен запрос синхронизации заказов ABCP. period_start=2026-03-24T15:36:42+00:00, period_end=2026-08-12T20:10:04.612913+00:00., {"period_start":"2026-03-24T15:36:42+00:00","period_end":"2026-08-12T20:10:04.612913+00:00","abcp_request_url":"unknown://cp/orders"}
Подготовлен запрос синхронизации заказов ABCP. period_start=2026-04-02T09:30:00+00:00, period_end=2026-08-12T20:10:04.612913+00:00., {"period_start":"2026-04-02T09:30:00+00:00","period_end":"2026-08-12T20:10:04.612913+00:00","abcp_request_url":"unknown://cp/orders"}
Синхронизация заказов завершена. records_loaded=3, period_start=2026-04-14T20:10:04.612913+00:00, period_end=2026-08-12T20:10:04.612913+00:00., {"records_loaded":3,"period_start":"2026-04-14T20:10:04.612913+00:00","period_end":"2026-08-12T20:10:04.612913+00:00"}
+
+ + \ No newline at end of file diff --git a/tests/artifacts/trace_logs/tests-test_app.py-test_order_sync_populates_snapshot-1.html b/tests/artifacts/trace_logs/tests-test_app.py-test_order_sync_populates_snapshot-1.html new file mode 100644 index 0000000..3d67d4f --- /dev/null +++ b/tests/artifacts/trace_logs/tests-test_app.py-test_order_sync_populates_snapshot-1.html @@ -0,0 +1,61 @@ + + + + + + Trace 78ed0674-e1fa-495e-a8f7-2d1eb189a4b2 + + + +
+
trace_id: 78ed0674-e1fa-495e-a8f7-2d1eb189a4b2
parent_id:
child_ids:
 
==============================
trace context: , 78ed0674-e1fa-495e-a8f7-2d1eb189a4b2
 
order-sync
 
Подготовлен запрос синхронизации заказов ABCP. period_start=2026-08-11T20:10:04.195237+00:00, period_end=2026-08-12T20:10:04.195237+00:00., {"period_start":"2026-08-11T20:10:04.195237+00:00","period_end":"2026-08-12T20:10:04.195237+00:00","abcp_request_url":"static://cp/orders"}
Подготовлен запрос синхронизации заказов ABCP. period_start=2026-08-12T20:10:04.193492+00:00, period_end=2026-08-12T20:10:04.195237+00:00., {"period_start":"2026-08-12T20:10:04.193492+00:00","period_end":"2026-08-12T20:10:04.195237+00:00","abcp_request_url":"static://cp/orders"}
Синхронизация заказов завершена. records_loaded=1, period_start=2026-08-11T20:10:04.195237+00:00, period_end=2026-08-12T20:10:04.195237+00:00., {"records_loaded":1,"period_start":"2026-08-11T20:10:04.195237+00:00","period_end":"2026-08-12T20:10:04.195237+00:00"}
+
+ + \ No newline at end of file diff --git a/tests/artifacts/trace_logs/tests-test_app.py-test_order_sync_rolling_lookback_ignores_existing_last_sync_time-1.html b/tests/artifacts/trace_logs/tests-test_app.py-test_order_sync_rolling_lookback_ignores_existing_last_sync_time-1.html new file mode 100644 index 0000000..12ae9b7 --- /dev/null +++ b/tests/artifacts/trace_logs/tests-test_app.py-test_order_sync_rolling_lookback_ignores_existing_last_sync_time-1.html @@ -0,0 +1,61 @@ + + + + + + Trace 3ec474af-77db-4d4e-82cc-0c9df0ae10c3 + + + +
+
trace_id: 3ec474af-77db-4d4e-82cc-0c9df0ae10c3
parent_id:
child_ids:
 
==============================
trace context: , 3ec474af-77db-4d4e-82cc-0c9df0ae10c3
 
order-sync
 
Подготовлен запрос синхронизации заказов ABCP. period_start=2026-04-14T20:10:04.605396+00:00, period_end=2026-08-12T20:10:04.605396+00:00., {"period_start":"2026-04-14T20:10:04.605396+00:00","period_end":"2026-08-12T20:10:04.605396+00:00","abcp_request_url":"unknown://cp/orders"}
Синхронизация заказов завершена без изменений. records_loaded=0, period_start=2026-04-14T20:10:04.605396+00:00, period_end=2026-08-12T20:10:04.605396+00:00., {"records_loaded":0,"period_start":"2026-04-14T20:10:04.605396+00:00","period_end":"2026-08-12T20:10:04.605396+00:00"}
+
+ + \ No newline at end of file diff --git a/tests/artifacts/trace_logs/tests-test_app.py-test_order_sync_waits_between_followup_abcp_requests-1.html b/tests/artifacts/trace_logs/tests-test_app.py-test_order_sync_waits_between_followup_abcp_requests-1.html new file mode 100644 index 0000000..b44d7f7 --- /dev/null +++ b/tests/artifacts/trace_logs/tests-test_app.py-test_order_sync_waits_between_followup_abcp_requests-1.html @@ -0,0 +1,61 @@ + + + + + + Trace c263b805-227c-4faf-855b-2d7bc51427e6 + + + +
+
trace_id: c263b805-227c-4faf-855b-2d7bc51427e6
parent_id:
child_ids:
 
==============================
trace context: , c263b805-227c-4faf-855b-2d7bc51427e6
 
order-sync
 
Подготовлен запрос синхронизации заказов ABCP. period_start=2026-04-14T20:10:04.620037+00:00, period_end=2026-08-12T20:10:04.620037+00:00., {"period_start":"2026-04-14T20:10:04.620037+00:00","period_end":"2026-08-12T20:10:04.620037+00:00","abcp_request_url":"unknown://cp/orders"}
Подготовлен запрос синхронизации заказов ABCP. period_start=2026-03-24T15:36:42+00:00, period_end=2026-08-12T20:10:04.620037+00:00., {"period_start":"2026-03-24T15:36:42+00:00","period_end":"2026-08-12T20:10:04.620037+00:00","abcp_request_url":"unknown://cp/orders"}
Подготовлен запрос синхронизации заказов ABCP. period_start=2026-04-02T09:30:00+00:00, period_end=2026-08-12T20:10:04.620037+00:00., {"period_start":"2026-04-02T09:30:00+00:00","period_end":"2026-08-12T20:10:04.620037+00:00","abcp_request_url":"unknown://cp/orders"}
Синхронизация заказов завершена. records_loaded=2, period_start=2026-04-14T20:10:04.620037+00:00, period_end=2026-08-12T20:10:04.620037+00:00., {"records_loaded":2,"period_start":"2026-04-14T20:10:04.620037+00:00","period_end":"2026-08-12T20:10:04.620037+00:00"}
+
+ + \ No newline at end of file diff --git a/tests/artifacts/trace_logs/tests-test_app.py-test_trace_messages_are_duplicated_to_console-1.html b/tests/artifacts/trace_logs/tests-test_app.py-test_trace_messages_are_duplicated_to_console-1.html new file mode 100644 index 0000000..d3a55d4 --- /dev/null +++ b/tests/artifacts/trace_logs/tests-test_app.py-test_trace_messages_are_duplicated_to_console-1.html @@ -0,0 +1,61 @@ + + + + + + Trace 2ca6de8b-8dbe-4ccd-b5a5-491739119228 + + + +
+
trace_id: 2ca6de8b-8dbe-4ccd-b5a5-491739119228
parent_id:
child_ids:
 
==============================
trace context: , 2ca6de8b-8dbe-4ccd-b5a5-491739119228
 
order-sync
 
Подготовлен запрос синхронизации заказов ABCP. period_start=2026-06-30T10:00:00+00:00, period_end=2026-06-30T11:00:00+00:00., {"period_start":"2026-06-30T10:00:00+00:00","period_end":"2026-06-30T11:00:00+00:00"}
+
+ + \ No newline at end of file diff --git a/tests/artifacts/trace_logs/tests-test_app.py-test_worker_unhandled_exception_is_logged_and_traced-1.html b/tests/artifacts/trace_logs/tests-test_app.py-test_worker_unhandled_exception_is_logged_and_traced-1.html new file mode 100644 index 0000000..96ec442 --- /dev/null +++ b/tests/artifacts/trace_logs/tests-test_app.py-test_worker_unhandled_exception_is_logged_and_traced-1.html @@ -0,0 +1,61 @@ + + + + + + Trace e4cae497-d728-498f-8216-651a44e59992 + + + +
+
trace_id: e4cae497-d728-498f-8216-651a44e59992
parent_id:
child_ids:
 
==============================
trace context: , e4cae497-d728-498f-8216-651a44e59992
 
worker-loop
 
Необработанное исключение воркера. worker_name=job-processing, error=RuntimeError('boom')., {"worker_name":"job-processing","error":"boom","exception_class":"RuntimeError"}
+
+ + \ No newline at end of file diff --git a/tests/test_trace_endpoint.py b/tests/test_trace_endpoint.py index 79c0d0e..b412933 100644 --- a/tests/test_trace_endpoint.py +++ b/tests/test_trace_endpoint.py @@ -80,8 +80,8 @@ def test_trace_endpoint_returns_html_by_default() -> None: levels=("ERROR", "WARNING", "INFO"), include_attrs_json=False, response_format="html", - ancestor_depth=None, - view="tree", + ancestor_depth=0, + view="single", ), ) ] @@ -333,13 +333,13 @@ def test_trace_endpoint_returns_html_page_with_related_links() -> None: assert "--error: #ff817d;" in response.text assert "--other: #ececec;" in response.text assert 'font: 13px/1.1 "SFMono-Regular", monospace;' in response.text - assert '
trace_id: trace-1
' in response.text - assert '
parent_id: parent-1
' in response.text + assert '
trace_id: trace-1
' in response.text + assert '
parent_id: parent-1
' in response.text assert '
child_ids:
' in response.text - assert '
- child-1
' in response.text - assert '
- child-2
' in response.text + assert '
- child-1
' in response.text + assert '
- child-2
' in response.text assert '
==============================
' in response.text - assert '
trace context: order_confirmation, trace-1
' in response.text + assert '
trace context: order_confirmation, trace-1
' in response.text assert '
load_stocks
' in response.text assert '
filter_stocks
' in response.text assert "loaded prices" in response.text @@ -450,43 +450,17 @@ def test_trace_endpoint_preserves_ancestor_depth_in_html_links() -> None: assert "parent warning" in response.text -def test_trace_endpoint_returns_full_tree_by_default() -> None: +def test_trace_endpoint_returns_single_view_by_default() -> None: async def trace_provider(trace_id: str, request: TraceQueryRequest) -> TraceLogView: assert trace_id == "child-1" - assert request.view == "tree" - assert request.ancestor_depth is None + assert request.view == "single" + assert request.ancestor_depth == 0 return TraceLogView( - trace_id="root-1", - parent_id=None, - alias="root", - child_ids=("parent-1",), - records=(_trace_record(row_id=1, level="INFO", message="root message"),), - descendants=( - TraceLogView( - trace_id="parent-1", - parent_id="root-1", - alias="parent", - child_ids=("child-1", "sibling-1"), - records=(_trace_record(row_id=2, level="WARNING", message="parent message"),), - descendants=( - TraceLogView( - trace_id="child-1", - parent_id="parent-1", - alias="child", - child_ids=(), - records=(_trace_record(row_id=3, level="ERROR", message="child message"),), - ), - TraceLogView( - trace_id="sibling-1", - parent_id="parent-1", - alias="sibling", - child_ids=(), - records=(_trace_record(row_id=4, level="INFO", message="sibling message"),), - ), - ), - ), - ), - requested_trace_id="child-1", + trace_id="child-1", + parent_id="parent-1", + alias="child", + child_ids=("leaf-1",), + records=(_trace_record(row_id=3, level="ERROR", message="child message"),), ) client = _build_client(trace_provider) @@ -496,8 +470,18 @@ def test_trace_endpoint_returns_full_tree_by_default() -> None: client.close() assert response.status_code == 200 - assert "requested_trace_id: child-1" in response.text - assert response.text.index("root message") < response.text.index("parent message") < response.text.index("child message") < response.text.index("sibling message") + assert response.text == ( + "trace_id: child-1\n" + "parent_id: parent-1\n" + "child_ids:\n" + " - leaf-1\n" + "\n" + "==============================\n" + "trace context: child, child-1\n" + "\n" + "step: process\n" + "child message" + ) def test_trace_endpoint_supports_single_view_opt_in() -> None: