Добавил просмотр trace логов полностью
This commit is contained in:
@@ -90,9 +90,12 @@ class TraceLogRecord:
|
||||
class TraceLogView:
|
||||
trace_id: str
|
||||
parent_id: str | None
|
||||
alias: str = ""
|
||||
child_ids: tuple[str, ...] = ()
|
||||
records: tuple[TraceLogRecord, ...] = ()
|
||||
ancestors: tuple[TraceLogView, ...] = ()
|
||||
descendants: tuple[TraceLogView, ...] = ()
|
||||
requested_trace_id: str | None = None
|
||||
|
||||
|
||||
class TraceLogReader(Protocol):
|
||||
@@ -101,5 +104,7 @@ class TraceLogReader(Protocol):
|
||||
trace_id: str,
|
||||
levels: tuple[TraceLevel, ...],
|
||||
ancestor_depth: int | None = 0,
|
||||
*,
|
||||
view: Literal["tree", "single"] = "tree",
|
||||
) -> TraceLogView | None:
|
||||
"""Load trace context and filtered log records."""
|
||||
|
||||
@@ -20,6 +20,7 @@ ActionResult = str | dict[str, object]
|
||||
ActionHandler = Callable[[ControlActionRequest], Awaitable[ActionResult]]
|
||||
HealthHandler = Callable[[], Awaitable[HealthPayload]]
|
||||
TraceResponseFormat = Literal["json", "text", "html"]
|
||||
TraceViewMode = Literal["tree", "single"]
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
@@ -27,7 +28,8 @@ class TraceQueryRequest:
|
||||
levels: tuple[TraceLevel, ...] = ("ERROR", "WARNING", "INFO")
|
||||
include_attrs_json: bool = False
|
||||
response_format: TraceResponseFormat = "html"
|
||||
ancestor_depth: int | None = 0
|
||||
ancestor_depth: int | None = None
|
||||
view: TraceViewMode = "tree"
|
||||
|
||||
|
||||
TraceLookupHandler = Callable[[str, TraceQueryRequest], Awaitable[TraceLogView]]
|
||||
|
||||
@@ -21,11 +21,16 @@ 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")
|
||||
view = raw_view.strip().lower()
|
||||
if view not in {"tree", "single"}:
|
||||
raise ValueError(f"unsupported trace view: {raw_view}")
|
||||
return TraceQueryRequest(
|
||||
levels=self._trace_levels(raw_levels),
|
||||
include_attrs_json=self._bool_param(request, "attrs_json") or False,
|
||||
response_format=response_format,
|
||||
ancestor_depth=self._ancestor_depth(request),
|
||||
ancestor_depth=self._ancestor_depth(request, default_all=view == "tree"),
|
||||
view=view,
|
||||
)
|
||||
|
||||
def _trace_levels(self, raw_levels: str | None) -> tuple[TraceLevel, ...]:
|
||||
@@ -51,10 +56,10 @@ class TraceRequestParser:
|
||||
return False
|
||||
raise ValueError(f"invalid boolean query parameter: {name}={raw_value}")
|
||||
|
||||
def _ancestor_depth(self, request: Request) -> int | None:
|
||||
def _ancestor_depth(self, request: Request, *, default_all: bool) -> int | None:
|
||||
raw_value = request.query_params.get("ancestor_depth")
|
||||
if raw_value is None:
|
||||
return 0
|
||||
return None if default_all else 0
|
||||
normalized = raw_value.strip().lower()
|
||||
if normalized == "all":
|
||||
return None
|
||||
@@ -69,26 +74,35 @@ class TraceRequestParser:
|
||||
|
||||
class TraceResponseRenderer:
|
||||
def render(self, trace_view: TraceLogView, request: TraceQueryRequest) -> Response:
|
||||
if request.view == "single":
|
||||
if request.response_format == "json":
|
||||
return self._render_json_single(trace_view, request)
|
||||
if request.response_format == "html":
|
||||
return self._render_html_single(trace_view, request)
|
||||
return self._render_text_single(trace_view, request)
|
||||
if request.response_format == "json":
|
||||
return self._render_json(trace_view, request)
|
||||
return self._render_json_tree(trace_view, request)
|
||||
if request.response_format == "html":
|
||||
return self._render_html(trace_view, request)
|
||||
return self._render_text(trace_view, request)
|
||||
return self._render_html_tree(trace_view, request)
|
||||
return self._render_text_tree(trace_view, request)
|
||||
|
||||
def _render_json(self, trace_view: TraceLogView, request: TraceQueryRequest) -> JSONResponse:
|
||||
def _render_json_single(self, trace_view: TraceLogView, request: TraceQueryRequest) -> JSONResponse:
|
||||
return JSONResponse(
|
||||
content={
|
||||
"trace_id": trace_view.trace_id,
|
||||
"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],
|
||||
"ancestors": [self._trace_payload(view, request) for view in trace_view.ancestors],
|
||||
"ancestors": [self._trace_payload_single(view, request) for view in trace_view.ancestors],
|
||||
}
|
||||
)
|
||||
|
||||
def _render_text(self, trace_view: TraceLogView, request: TraceQueryRequest) -> PlainTextResponse:
|
||||
def _render_json_tree(self, trace_view: TraceLogView, request: TraceQueryRequest) -> JSONResponse:
|
||||
return JSONResponse(content=self._trace_payload_tree(trace_view, request))
|
||||
|
||||
def _render_text_single(self, trace_view: TraceLogView, request: TraceQueryRequest) -> PlainTextResponse:
|
||||
lineage = [*trace_view.ancestors, trace_view]
|
||||
lines = self._text_trace_summary_lines(trace_view)
|
||||
lines = self._text_trace_summary_lines_single(trace_view)
|
||||
for index, entry in enumerate(lineage):
|
||||
if index == 0:
|
||||
lines.append("")
|
||||
@@ -97,10 +111,29 @@ class TraceResponseRenderer:
|
||||
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:
|
||||
def _render_text_tree(self, trace_view: TraceLogView, request: TraceQueryRequest) -> PlainTextResponse:
|
||||
lines = self._text_trace_summary_lines_tree(trace_view)
|
||||
for index, entry in enumerate(self._tree_walk(trace_view)):
|
||||
if index == 0:
|
||||
lines.append("")
|
||||
else:
|
||||
lines.extend(["", ""])
|
||||
lines.extend(self._text_trace_log_lines(entry, request))
|
||||
return PlainTextResponse(content="\n".join(lines))
|
||||
|
||||
def _render_html_single(self, trace_view: TraceLogView, request: TraceQueryRequest) -> HTMLResponse:
|
||||
title = escape(f"Trace {trace_view.trace_id}")
|
||||
lines = self._html_lines(trace_view, request)
|
||||
html = f"""<!DOCTYPE html>
|
||||
lines = self._html_lines_single(trace_view, request)
|
||||
return HTMLResponse(content=self._html_document(title, lines))
|
||||
|
||||
def _render_html_tree(self, trace_view: TraceLogView, request: TraceQueryRequest) -> HTMLResponse:
|
||||
requested = trace_view.requested_trace_id or trace_view.trace_id
|
||||
title = escape(f"Trace {requested}")
|
||||
lines = self._html_lines_tree(trace_view, request)
|
||||
return HTMLResponse(content=self._html_document(title, lines))
|
||||
|
||||
def _html_document(self, title: str, lines: str) -> str:
|
||||
return f'''<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
@@ -160,8 +193,7 @@ class TraceResponseRenderer:
|
||||
{lines}
|
||||
</div>
|
||||
</body>
|
||||
</html>"""
|
||||
return HTMLResponse(content=html)
|
||||
</html>'''
|
||||
|
||||
def _child_id_lines(self, child_ids: tuple[str, ...]) -> list[str]:
|
||||
lines = ["child_ids:"]
|
||||
@@ -178,6 +210,7 @@ class TraceResponseRenderer:
|
||||
"format": "html",
|
||||
"levels": ",".join(request.levels),
|
||||
"attrs_json": "true" if request.include_attrs_json else "false",
|
||||
"view": request.view,
|
||||
}
|
||||
if request.ancestor_depth is None:
|
||||
params["ancestor_depth"] = "all"
|
||||
@@ -186,9 +219,9 @@ class TraceResponseRenderer:
|
||||
query = urlencode(params)
|
||||
return f"/traces/{trace_id}?{query}"
|
||||
|
||||
def _html_lines(self, trace_view: TraceLogView, request: TraceQueryRequest) -> str:
|
||||
def _html_lines_single(self, trace_view: TraceLogView, request: TraceQueryRequest) -> str:
|
||||
lineage = [*trace_view.ancestors, trace_view]
|
||||
lines = self._html_trace_summary_lines(trace_view, request)
|
||||
lines = self._html_trace_summary_lines_single(trace_view, request)
|
||||
for index, entry in enumerate(lineage):
|
||||
if index == 0:
|
||||
lines.append(self._html_plain_line(""))
|
||||
@@ -197,7 +230,17 @@ class TraceResponseRenderer:
|
||||
lines.extend(self._html_trace_log_lines(entry, request))
|
||||
return "".join(lines)
|
||||
|
||||
def _trace_payload(self, trace_view: TraceLogView, request: TraceQueryRequest) -> dict[str, object]:
|
||||
def _html_lines_tree(self, trace_view: TraceLogView, request: TraceQueryRequest) -> str:
|
||||
lines = self._html_trace_summary_lines_tree(trace_view, request)
|
||||
for index, entry in enumerate(self._tree_walk(trace_view)):
|
||||
if index == 0:
|
||||
lines.append(self._html_plain_line(""))
|
||||
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_single(self, trace_view: TraceLogView, request: TraceQueryRequest) -> dict[str, object]:
|
||||
return {
|
||||
"trace_id": trace_view.trace_id,
|
||||
"parent_id": trace_view.parent_id or "",
|
||||
@@ -205,17 +248,38 @@ class TraceResponseRenderer:
|
||||
"messages": [record.as_dict(include_attrs_json=request.include_attrs_json) for record in trace_view.records],
|
||||
}
|
||||
|
||||
def _text_trace_summary_lines(self, trace_view: TraceLogView) -> list[str]:
|
||||
def _trace_payload_tree(self, trace_view: TraceLogView, request: TraceQueryRequest) -> dict[str, object]:
|
||||
payload: dict[str, object] = {
|
||||
"trace_id": trace_view.trace_id,
|
||||
"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],
|
||||
"descendants": [self._trace_payload_tree(view, request) for view in trace_view.descendants],
|
||||
}
|
||||
if trace_view.requested_trace_id and trace_view.requested_trace_id != trace_view.trace_id:
|
||||
payload["requested_trace_id"] = trace_view.requested_trace_id
|
||||
return payload
|
||||
|
||||
def _text_trace_summary_lines_single(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_summary_lines_tree(self, trace_view: TraceLogView) -> list[str]:
|
||||
lines = [f"trace_id: {trace_view.trace_id}"]
|
||||
requested = trace_view.requested_trace_id or trace_view.trace_id
|
||||
if requested != trace_view.trace_id:
|
||||
lines.append(f"requested_trace_id: {requested}")
|
||||
lines.append(f"parent_id: {trace_view.parent_id or ''}")
|
||||
lines.extend(self._child_id_lines(trace_view.child_ids))
|
||||
return lines
|
||||
|
||||
def _text_trace_log_lines(self, trace_view: TraceLogView, request: TraceQueryRequest) -> list[str]:
|
||||
lines = [
|
||||
TRACE_SECTION_SEPARATOR,
|
||||
f"trace_id: {trace_view.trace_id}",
|
||||
self._trace_context_label(trace_view),
|
||||
"",
|
||||
]
|
||||
previous_step: str | None = None
|
||||
@@ -230,7 +294,7 @@ class TraceResponseRenderer:
|
||||
lines.append(self._text_message(record, request.include_attrs_json))
|
||||
return lines
|
||||
|
||||
def _html_trace_summary_lines(self, trace_view: TraceLogView, request: TraceQueryRequest) -> list[str]:
|
||||
def _html_trace_summary_lines_single(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)}"),
|
||||
@@ -238,10 +302,24 @@ class TraceResponseRenderer:
|
||||
*(self._html_plain_line(f" - {self._trace_link(child_id, request)}") for child_id in trace_view.child_ids),
|
||||
]
|
||||
|
||||
def _html_trace_summary_lines_tree(self, trace_view: TraceLogView, request: TraceQueryRequest) -> list[str]:
|
||||
lines = [self._html_plain_line(f"trace_id: {self._trace_link(trace_view.trace_id, request)}")]
|
||||
requested = trace_view.requested_trace_id or trace_view.trace_id
|
||||
if requested != trace_view.trace_id:
|
||||
lines.append(self._html_plain_line(f"requested_trace_id: {self._trace_link(requested, request)}"))
|
||||
lines.extend(
|
||||
[
|
||||
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),
|
||||
]
|
||||
)
|
||||
return lines
|
||||
|
||||
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(self._html_trace_context_label(trace_view, request)),
|
||||
self._html_plain_line(""),
|
||||
]
|
||||
previous_step: str | None = None
|
||||
@@ -260,29 +338,36 @@ class TraceResponseRenderer:
|
||||
return lines
|
||||
|
||||
def _html_message_lines(self, record: TraceLogRecord, include_attrs_json: bool) -> list[str]:
|
||||
lines = [self._html_colored_line(self._text_message(record, include_attrs_json), record.level)]
|
||||
return lines
|
||||
return [self._html_colored_line(self._text_message(record, include_attrs_json), record.level)]
|
||||
|
||||
def _html_plain_line(self, content: str) -> str:
|
||||
return f"<div class=\"line\">{content or ' '}</div>"
|
||||
return f'<div class="line">{content or " "}</div>'
|
||||
|
||||
def _html_step_line(self, content: str) -> str:
|
||||
return f"<div class=\"line\" style=\"color: var(--step);\">{escape(content) or ' '}</div>"
|
||||
return f'<div class="line" style="color: var(--step);">{escape(content) or " "}</div>'
|
||||
|
||||
def _html_colored_line(self, content: str, level: str) -> str:
|
||||
level_class = self._level_class(level)
|
||||
return f"<div class=\"line {level_class}\">{escape(content)}</div>"
|
||||
return f'<div class="line {level_class}">{escape(content)}</div>'
|
||||
|
||||
def _trace_link(self, trace_id: str, request: TraceQueryRequest) -> str:
|
||||
href = escape(self._trace_href(trace_id, request), quote=True)
|
||||
text = escape(trace_id)
|
||||
return f"<a href=\"{href}\">{text}</a>"
|
||||
return f'<a href="{href}">{text}</a>'
|
||||
|
||||
def _optional_trace_link(self, trace_id: str | None, request: TraceQueryRequest) -> str:
|
||||
if not trace_id:
|
||||
return ""
|
||||
return self._trace_link(trace_id, request)
|
||||
|
||||
def _trace_context_label(self, trace_view: TraceLogView) -> str:
|
||||
return f"trace context: {trace_view.alias}, {trace_view.trace_id}"
|
||||
|
||||
def _html_trace_context_label(self, trace_view: TraceLogView, request: TraceQueryRequest) -> str:
|
||||
alias = escape(trace_view.alias)
|
||||
trace_link = self._trace_link(trace_view.trace_id, request)
|
||||
return f"trace context: {alias}, {trace_link}"
|
||||
|
||||
def _level_class(self, level: str) -> str:
|
||||
if level == "ERROR":
|
||||
return "msg-error"
|
||||
@@ -291,3 +376,14 @@ class TraceResponseRenderer:
|
||||
if level == "INFO":
|
||||
return "msg-info"
|
||||
return "msg-debug"
|
||||
|
||||
def _tree_walk(self, trace_view: TraceLogView) -> list[TraceLogView]:
|
||||
ordered: list[TraceLogView] = []
|
||||
|
||||
def _visit(node: TraceLogView) -> None:
|
||||
ordered.append(node)
|
||||
for child in node.descendants:
|
||||
_visit(child)
|
||||
|
||||
_visit(trace_view)
|
||||
return ordered
|
||||
|
||||
@@ -141,7 +141,7 @@ class RuntimeManager:
|
||||
reader = build_trace_log_reader(self.traces.transport)
|
||||
if reader is None:
|
||||
raise RuntimeError("trace log reader is not configured")
|
||||
trace_view = reader.read_trace(trace_id, request.levels, request.ancestor_depth)
|
||||
trace_view = reader.read_trace(trace_id, request.levels, request.ancestor_depth, view=request.view)
|
||||
if trace_view is None:
|
||||
raise KeyError(trace_id)
|
||||
return trace_view
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from typing import Any
|
||||
from typing import Any, Literal
|
||||
|
||||
from app_runtime.contracts.trace import TraceLevel, TraceLogReader, TraceLogRecord, TraceLogView, TraceTransport
|
||||
from app_runtime.tracing.transport import MySqlTraceConnectionFactory, MySqlTraceTransport
|
||||
|
||||
TraceViewMode = Literal["tree", "single"]
|
||||
|
||||
|
||||
class MySqlTraceLogReader(TraceLogReader):
|
||||
def __init__(self, connection_factory: MySqlTraceConnectionFactory) -> None:
|
||||
@@ -16,19 +18,72 @@ class MySqlTraceLogReader(TraceLogReader):
|
||||
trace_id: str,
|
||||
levels: tuple[TraceLevel, ...],
|
||||
ancestor_depth: int | None = 0,
|
||||
*,
|
||||
view: TraceViewMode = "tree",
|
||||
) -> TraceLogView | None:
|
||||
parent_id = self._read_parent_id(trace_id)
|
||||
if parent_id is None and not self._trace_exists(trace_id):
|
||||
context = self._read_context(trace_id)
|
||||
if context is None:
|
||||
return None
|
||||
ancestors = self._read_ancestors(parent_id, levels, ancestor_depth)
|
||||
parent_id, alias = context
|
||||
if view == "single":
|
||||
ancestors = self._read_ancestors(parent_id, levels, ancestor_depth)
|
||||
child_ids = self._read_child_ids(trace_id)
|
||||
records = self._read_records(trace_id, levels)
|
||||
return TraceLogView(
|
||||
trace_id=trace_id,
|
||||
parent_id=parent_id,
|
||||
alias=alias,
|
||||
child_ids=tuple(child_ids),
|
||||
records=tuple(records),
|
||||
ancestors=tuple(ancestors),
|
||||
)
|
||||
root_trace_id = self._resolve_tree_root(trace_id, ancestor_depth)
|
||||
root_view = self._read_tree(root_trace_id, levels)
|
||||
if root_view is None:
|
||||
return None
|
||||
if root_trace_id == trace_id:
|
||||
return root_view
|
||||
return TraceLogView(
|
||||
trace_id=root_view.trace_id,
|
||||
parent_id=root_view.parent_id,
|
||||
alias=root_view.alias,
|
||||
child_ids=root_view.child_ids,
|
||||
records=root_view.records,
|
||||
ancestors=root_view.ancestors,
|
||||
descendants=root_view.descendants,
|
||||
requested_trace_id=trace_id,
|
||||
)
|
||||
|
||||
def _resolve_tree_root(self, trace_id: str, ancestor_depth: int | None) -> str:
|
||||
current_trace_id = trace_id
|
||||
remaining_depth = ancestor_depth
|
||||
while True:
|
||||
context = self._read_context(current_trace_id)
|
||||
if context is None:
|
||||
return current_trace_id
|
||||
parent_id, _alias = context
|
||||
if parent_id is None:
|
||||
return current_trace_id
|
||||
if remaining_depth == 0:
|
||||
return current_trace_id
|
||||
current_trace_id = parent_id
|
||||
if remaining_depth is not None:
|
||||
remaining_depth -= 1
|
||||
|
||||
def _read_tree(self, trace_id: str, levels: tuple[TraceLevel, ...]) -> TraceLogView | None:
|
||||
context = self._read_context(trace_id)
|
||||
if context is None:
|
||||
return None
|
||||
parent_id, alias = context
|
||||
child_ids = self._read_child_ids(trace_id)
|
||||
records = self._read_records(trace_id, levels)
|
||||
descendants = [self._read_tree(child_id, levels) for child_id in child_ids]
|
||||
return TraceLogView(
|
||||
trace_id=trace_id,
|
||||
parent_id=parent_id,
|
||||
alias=alias,
|
||||
child_ids=tuple(child_ids),
|
||||
records=tuple(records),
|
||||
ancestors=tuple(ancestors),
|
||||
records=tuple(self._read_records(trace_id, levels)),
|
||||
descendants=tuple(view for view in descendants if view is not None),
|
||||
)
|
||||
|
||||
def _read_ancestors(
|
||||
@@ -43,11 +98,15 @@ class MySqlTraceLogReader(TraceLogReader):
|
||||
ancestors: list[TraceLogView] = []
|
||||
current_trace_id = parent_id
|
||||
while current_trace_id is not None and (remaining_depth is None or remaining_depth > 0):
|
||||
current_parent_id = self._read_parent_id(current_trace_id)
|
||||
context = self._read_context(current_trace_id)
|
||||
if context is None:
|
||||
break
|
||||
current_parent_id, alias = context
|
||||
ancestors.append(
|
||||
TraceLogView(
|
||||
trace_id=current_trace_id,
|
||||
parent_id=current_parent_id,
|
||||
alias=alias,
|
||||
child_ids=tuple(self._read_child_ids(current_trace_id)),
|
||||
records=tuple(self._read_records(current_trace_id, levels)),
|
||||
)
|
||||
@@ -58,22 +117,15 @@ class MySqlTraceLogReader(TraceLogReader):
|
||||
ancestors.reverse()
|
||||
return ancestors
|
||||
|
||||
def _trace_exists(self, trace_id: str) -> bool:
|
||||
query = "SELECT 1 FROM trace_contexts WHERE trace_id = %s"
|
||||
with self._connection_factory.connect() as connection:
|
||||
with connection.cursor() as cursor:
|
||||
cursor.execute(query, (trace_id,))
|
||||
return cursor.fetchone() is not None
|
||||
|
||||
def _read_parent_id(self, trace_id: str) -> str | None:
|
||||
query = "SELECT parent_id FROM trace_contexts WHERE trace_id = %s"
|
||||
def _read_context(self, trace_id: str) -> tuple[str | None, str] | None:
|
||||
query = "SELECT parent_id, alias FROM trace_contexts WHERE trace_id = %s"
|
||||
with self._connection_factory.connect() as connection:
|
||||
with connection.cursor() as cursor:
|
||||
cursor.execute(query, (trace_id,))
|
||||
row = cursor.fetchone()
|
||||
if row is None:
|
||||
return None
|
||||
return self._string_or_none(row.get("parent_id"))
|
||||
return self._string_or_none(row.get("parent_id")), self._string_or_none(row.get("alias")) or ""
|
||||
|
||||
def _read_records(self, trace_id: str, levels: tuple[TraceLevel, ...]) -> list[TraceLogRecord]:
|
||||
placeholders = ", ".join(["%s"] * len(levels))
|
||||
|
||||
Reference in New Issue
Block a user