Добавил дочерние trace_ids
This commit is contained in:
+1
-1
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "plba"
|
name = "plba"
|
||||||
version = "0.3.1"
|
version = "0.3.2"
|
||||||
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"
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ class TraceLogRecord:
|
|||||||
class TraceLogView:
|
class TraceLogView:
|
||||||
trace_id: str
|
trace_id: str
|
||||||
parent_id: str | None
|
parent_id: str | None
|
||||||
|
child_ids: tuple[str, ...] = ()
|
||||||
records: tuple[TraceLogRecord, ...] = ()
|
records: tuple[TraceLogRecord, ...] = ()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -137,17 +137,26 @@ class HttpControlAppFactory:
|
|||||||
content={
|
content={
|
||||||
"trace_id": trace_view.trace_id,
|
"trace_id": trace_view.trace_id,
|
||||||
"parent_id": trace_view.parent_id or "",
|
"parent_id": trace_view.parent_id or "",
|
||||||
|
"child_ids": list(trace_view.child_ids),
|
||||||
"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],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
lines = [
|
lines = [
|
||||||
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 ''}",
|
||||||
"--------------------------------------------------",
|
|
||||||
]
|
]
|
||||||
|
lines.extend(self._child_id_lines(trace_view.child_ids))
|
||||||
|
lines.append("--------------------------------------------------")
|
||||||
for record in trace_view.records:
|
for record in trace_view.records:
|
||||||
line = record.message
|
line = record.message
|
||||||
if request.include_attrs_json:
|
if request.include_attrs_json:
|
||||||
line = f"{line}, {json.dumps(record.attrs_json, ensure_ascii=False, separators=(',', ':'))}"
|
line = f"{line}, {json.dumps(record.attrs_json, ensure_ascii=False, separators=(',', ':'))}"
|
||||||
lines.append(line)
|
lines.append(line)
|
||||||
return PlainTextResponse(content="\n".join(lines))
|
return PlainTextResponse(content="\n".join(lines))
|
||||||
|
|
||||||
|
def _child_id_lines(self, child_ids: tuple[str, ...]) -> list[str]:
|
||||||
|
lines = ["child_ids:"]
|
||||||
|
if not child_ids:
|
||||||
|
return lines
|
||||||
|
lines.extend(f" - {child_id}" for child_id in child_ids)
|
||||||
|
return lines
|
||||||
|
|||||||
@@ -15,8 +15,14 @@ class MySqlTraceLogReader(TraceLogReader):
|
|||||||
parent_id = self._read_parent_id(trace_id)
|
parent_id = self._read_parent_id(trace_id)
|
||||||
if parent_id is None and not self._trace_exists(trace_id):
|
if parent_id is None and not self._trace_exists(trace_id):
|
||||||
return None
|
return None
|
||||||
|
child_ids = self._read_child_ids(trace_id)
|
||||||
records = self._read_records(trace_id, levels)
|
records = self._read_records(trace_id, levels)
|
||||||
return TraceLogView(trace_id=trace_id, parent_id=parent_id, records=tuple(records))
|
return TraceLogView(
|
||||||
|
trace_id=trace_id,
|
||||||
|
parent_id=parent_id,
|
||||||
|
child_ids=tuple(child_ids),
|
||||||
|
records=tuple(records),
|
||||||
|
)
|
||||||
|
|
||||||
def _trace_exists(self, trace_id: str) -> bool:
|
def _trace_exists(self, trace_id: str) -> bool:
|
||||||
query = "SELECT 1 FROM trace_contexts WHERE trace_id = %s"
|
query = "SELECT 1 FROM trace_contexts WHERE trace_id = %s"
|
||||||
@@ -50,6 +56,19 @@ class MySqlTraceLogReader(TraceLogReader):
|
|||||||
rows = cursor.fetchall()
|
rows = cursor.fetchall()
|
||||||
return [self._build_record(row) for row in rows]
|
return [self._build_record(row) for row in rows]
|
||||||
|
|
||||||
|
def _read_child_ids(self, trace_id: str) -> list[str]:
|
||||||
|
query = """
|
||||||
|
SELECT trace_id
|
||||||
|
FROM trace_contexts
|
||||||
|
WHERE parent_id = %s
|
||||||
|
ORDER BY event_time ASC, trace_id ASC
|
||||||
|
"""
|
||||||
|
with self._connection_factory.connect() as connection:
|
||||||
|
with connection.cursor() as cursor:
|
||||||
|
cursor.execute(query, (trace_id,))
|
||||||
|
rows = cursor.fetchall()
|
||||||
|
return [str(row["trace_id"]) for row in rows]
|
||||||
|
|
||||||
def _build_record(self, row: dict[str, Any]) -> TraceLogRecord:
|
def _build_record(self, row: dict[str, Any]) -> TraceLogRecord:
|
||||||
return TraceLogRecord(
|
return TraceLogRecord(
|
||||||
id=int(row["id"]),
|
id=int(row["id"]),
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ def test_trace_endpoint_returns_text_with_default_levels() -> None:
|
|||||||
return TraceLogView(
|
return TraceLogView(
|
||||||
trace_id="trace-1",
|
trace_id="trace-1",
|
||||||
parent_id="root-trace",
|
parent_id="root-trace",
|
||||||
|
child_ids=("child-1", "child-2"),
|
||||||
records=(
|
records=(
|
||||||
_trace_record(row_id=1, level="ERROR", message="first error"),
|
_trace_record(row_id=1, level="ERROR", message="first error"),
|
||||||
_trace_record(row_id=2, level="WARNING", message="second warning"),
|
_trace_record(row_id=2, level="WARNING", message="second warning"),
|
||||||
@@ -68,6 +69,9 @@ def test_trace_endpoint_returns_text_with_default_levels() -> None:
|
|||||||
assert response.text == (
|
assert response.text == (
|
||||||
"trace_id: trace-1\n"
|
"trace_id: trace-1\n"
|
||||||
"parent_id: root-trace\n"
|
"parent_id: root-trace\n"
|
||||||
|
"child_ids:\n"
|
||||||
|
" - child-1\n"
|
||||||
|
" - child-2\n"
|
||||||
"--------------------------------------------------\n"
|
"--------------------------------------------------\n"
|
||||||
"first error\n"
|
"first error\n"
|
||||||
"second warning"
|
"second warning"
|
||||||
@@ -80,6 +84,7 @@ def test_trace_endpoint_appends_attrs_json_in_text_mode() -> None:
|
|||||||
return TraceLogView(
|
return TraceLogView(
|
||||||
trace_id="trace-1",
|
trace_id="trace-1",
|
||||||
parent_id=None,
|
parent_id=None,
|
||||||
|
child_ids=(),
|
||||||
records=(
|
records=(
|
||||||
_trace_record(row_id=1, level="ERROR", message="failure", attrs_json={"attempt": 2, "source": "crm"}),
|
_trace_record(row_id=1, level="ERROR", message="failure", attrs_json={"attempt": 2, "source": "crm"}),
|
||||||
),
|
),
|
||||||
@@ -95,6 +100,7 @@ def test_trace_endpoint_appends_attrs_json_in_text_mode() -> None:
|
|||||||
assert response.text == (
|
assert response.text == (
|
||||||
"trace_id: trace-1\n"
|
"trace_id: trace-1\n"
|
||||||
"parent_id: \n"
|
"parent_id: \n"
|
||||||
|
"child_ids:\n"
|
||||||
"--------------------------------------------------\n"
|
"--------------------------------------------------\n"
|
||||||
'failure, {"attempt":2,"source":"crm"}'
|
'failure, {"attempt":2,"source":"crm"}'
|
||||||
)
|
)
|
||||||
@@ -105,6 +111,7 @@ def test_trace_endpoint_returns_json_payload() -> None:
|
|||||||
return TraceLogView(
|
return TraceLogView(
|
||||||
trace_id="trace-1",
|
trace_id="trace-1",
|
||||||
parent_id="parent-1",
|
parent_id="parent-1",
|
||||||
|
child_ids=("child-1",),
|
||||||
records=(
|
records=(
|
||||||
_trace_record(row_id=3, level="INFO", message="done", attrs_json={"batch": 7}),
|
_trace_record(row_id=3, level="INFO", message="done", attrs_json={"batch": 7}),
|
||||||
),
|
),
|
||||||
@@ -120,6 +127,7 @@ def test_trace_endpoint_returns_json_payload() -> None:
|
|||||||
assert response.json() == {
|
assert response.json() == {
|
||||||
"trace_id": "trace-1",
|
"trace_id": "trace-1",
|
||||||
"parent_id": "parent-1",
|
"parent_id": "parent-1",
|
||||||
|
"child_ids": ["child-1"],
|
||||||
"messages": [
|
"messages": [
|
||||||
{
|
{
|
||||||
"id": 3,
|
"id": 3,
|
||||||
@@ -150,7 +158,12 @@ def test_trace_endpoint_validates_query_params() -> None:
|
|||||||
|
|
||||||
|
|
||||||
def test_runtime_trace_logs_uses_configured_reader(monkeypatch) -> None:
|
def test_runtime_trace_logs_uses_configured_reader(monkeypatch) -> None:
|
||||||
expected = TraceLogView(trace_id="trace-1", parent_id="root", records=(_trace_record(row_id=1, level="ERROR", message="boom"),))
|
expected = TraceLogView(
|
||||||
|
trace_id="trace-1",
|
||||||
|
parent_id="root",
|
||||||
|
child_ids=("child-1",),
|
||||||
|
records=(_trace_record(row_id=1, level="ERROR", message="boom"),),
|
||||||
|
)
|
||||||
|
|
||||||
class StubReader:
|
class StubReader:
|
||||||
def read_trace(self, trace_id: str, levels: tuple[str, ...]) -> TraceLogView | None:
|
def read_trace(self, trace_id: str, levels: tuple[str, ...]) -> TraceLogView | None:
|
||||||
@@ -170,14 +183,18 @@ def test_mysql_trace_log_reader_maps_db_rows() -> None:
|
|||||||
class FakeCursor:
|
class FakeCursor:
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.executed: list[tuple[str, tuple[object, ...]]] = []
|
self.executed: list[tuple[str, tuple[object, ...]]] = []
|
||||||
|
self._current_query = ""
|
||||||
|
|
||||||
def execute(self, query: str, params: tuple[object, ...]) -> None:
|
def execute(self, query: str, params: tuple[object, ...]) -> None:
|
||||||
self.executed.append((query, params))
|
self.executed.append((query, params))
|
||||||
|
self._current_query = query
|
||||||
|
|
||||||
def fetchone(self) -> dict[str, object] | None:
|
def fetchone(self) -> dict[str, object] | None:
|
||||||
return {"parent_id": "root-77"}
|
return {"parent_id": "root-77"}
|
||||||
|
|
||||||
def fetchall(self) -> list[dict[str, object]]:
|
def fetchall(self) -> list[dict[str, object]]:
|
||||||
|
if "WHERE parent_id = %s" in self._current_query:
|
||||||
|
return [{"trace_id": "child-1"}, {"trace_id": "child-2"}]
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
"id": 8,
|
"id": 8,
|
||||||
@@ -225,6 +242,7 @@ def test_mysql_trace_log_reader_maps_db_rows() -> None:
|
|||||||
assert view == TraceLogView(
|
assert view == TraceLogView(
|
||||||
trace_id="trace-1",
|
trace_id="trace-1",
|
||||||
parent_id="root-77",
|
parent_id="root-77",
|
||||||
|
child_ids=("child-1", "child-2"),
|
||||||
records=(
|
records=(
|
||||||
TraceLogRecord(
|
TraceLogRecord(
|
||||||
id=8,
|
id=8,
|
||||||
@@ -238,5 +256,6 @@ def test_mysql_trace_log_reader_maps_db_rows() -> None:
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
assert len(factory.cursor.executed) == 2
|
assert len(factory.cursor.executed) == 3
|
||||||
assert factory.cursor.executed[1][1] == ("trace-1", "ERROR", "WARNING")
|
assert factory.cursor.executed[1][1] == ("trace-1",)
|
||||||
|
assert factory.cursor.executed[2][1] == ("trace-1", "ERROR", "WARNING")
|
||||||
|
|||||||
Reference in New Issue
Block a user