Skip to content

Commit 3030f72

Browse files
abhizipstackclaude
andauthored
FIX: Reduce logs panel noise and make execution messages readable (OR-1457) (#59)
* fix: reduce logs-panel noise and make messages readable (OR-1457) Layer 1 — silence the worst offender - Demote UsingCachedObject from InfoLevel to DebugLevel so the per-query "Using Cached Object of bigquery_connection_object" spam no longer reaches the UI at default verbosity. Layer 2A — fix data bugs leaking dev internals into the UI - ProcessingModel: avoid Processing Model : "" by using cls.__name__ with file_name as a fallback, instead of str(cls) which can be empty for dynamically generated no-code model classes. - ExecStatus / Materialization: stop str(Enum) from leaking the dotted enum names (ExecStatus.Success, Materialization.EPHEMERAL) into user messages; use .value so the UI shows SUCCESS / OK / TABLE. - Summary counts: parse_and_fire_reports compared end_status against ExecStatus.Success/Error but end_status is set to OK/Fail, so pass/error counters stayed zero. Compare against the correct end-status values so DONE PASS=N now reflects reality. Frontend - Socket handler now captures the level alongside the message; each log entry is stored as { level, message } instead of a raw string. - New log-level selector in the bottom logs tab: All logs / Info & above / Warnings & errors / Errors only Default is "Info & above" (hides debug noise out of the box). Choice persists in localStorage under visitran.logsLevel. - Log rows are tinted by severity (grey/default/amber/red) for quick scanning when All is selected. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address Greptile review — socket cleanup leak + memoize log filter Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: use antd theme token for log-panel border color Replaced hardcoded #f0f0f0 border with token.colorBorderSecondary so the log-level dropdown separator renders correctly in both light and dark themes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent b071add commit 3030f72

4 files changed

Lines changed: 96 additions & 43 deletions

File tree

backend/visitran/events/printer.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,13 +86,13 @@ def parse_and_fire_reports() -> None:
8686
status=iterate_result.status,
8787
)
8888
)
89-
if iterate_result.end_status == str(ExecStatus.Success):
89+
if iterate_result.end_status == ExecStatus.OK.value:
9090
pass_count += 1
91-
if iterate_result.end_status == str(ExecStatus.Warn):
91+
elif iterate_result.end_status == ExecStatus.Warn.value:
9292
warn_count += 1
93-
if iterate_result.end_status == str(ExecStatus.Error):
93+
elif iterate_result.end_status == ExecStatus.Fail.value:
9494
error_count += 1
95-
if iterate_result.end_status == str(ExecStatus.Skipped):
95+
elif iterate_result.end_status == ExecStatus.Skipped.value:
9696
skip_count += 1
9797

9898
functions.fire_event(

backend/visitran/events/types.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -469,7 +469,7 @@ def message(self) -> str:
469469

470470

471471
@dataclass
472-
class UsingCachedObject(InfoLevel, proto_type.UsingCachedObject):
472+
class UsingCachedObject(DebugLevel, proto_type.UsingCachedObject):
473473
def code(self) -> str:
474474
return "Z009"
475475

backend/visitran/visitran.py

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ def execute_graph(self) -> None:
260260
destination_table_obj=(
261261
str(node.destination_table_obj) if hasattr(node, "destination_table_obj") else ""
262262
),
263-
materialization=str(node.materialization),
263+
materialization=node.materialization.value if hasattr(node.materialization, "value") else str(node.materialization),
264264
select_statement=(str(node.select_statement) if hasattr(node, "select_statement") else ""),
265265
source_schema_name=node.source_schema_name,
266266
source_table_name=node.source_table_name,
@@ -276,8 +276,8 @@ def execute_graph(self) -> None:
276276
ending_time=datetime.datetime.now(),
277277
failures=False,
278278
info_message=f"Running {node_name}",
279-
status=str(ExecStatus.Success),
280-
end_status=str(ExecStatus.OK),
279+
status=ExecStatus.Success.value,
280+
end_status=ExecStatus.OK.value,
281281
)
282282
sequence_number += 1
283283
BASE_RESULT.append(base_result)
@@ -291,8 +291,8 @@ def execute_graph(self) -> None:
291291
node_name=str(node_name),
292292
sequence_num=sequence_number,
293293
ending_time=datetime.datetime.now(),
294-
status=str(ExecStatus.Error),
295-
end_status=str(ExecStatus.Fail),
294+
status=ExecStatus.Error.value,
295+
end_status=ExecStatus.Fail.value,
296296
info_message=f"Error occurred while running {node_name}",
297297
failures=True,
298298
)
@@ -438,7 +438,7 @@ def search_n_run_models(self, model_name: str = None, model_names: list = None)
438438
)
439439

440440
for cls in cls_set:
441-
fire_event(ProcessingModel(cls=str(cls)))
441+
fire_event(ProcessingModel(cls=getattr(cls, "__name__", "") or file_name))
442442
# process each model only once
443443
# they might be imported in several places!
444444

@@ -550,9 +550,9 @@ def search_n_run_tests(self) -> None:
550550
sequence_num=test_files.index(tf),
551551
failures=True,
552552
ending_time=datetime.datetime.now(),
553-
status=str(ExecStatus.Error),
553+
status=ExecStatus.Error.value,
554554
info_message=f"Error occured in {tf} execution",
555-
end_status=str(ExecStatus.Fail),
555+
end_status=ExecStatus.Fail.value,
556556
)
557557
BASE_RESULT.append(base_result)
558558
parse_and_fire_reports()
@@ -611,8 +611,8 @@ def _run_tests(self, tf: str, tst_cls: Any, tst_obj: Any, test_methods: list[str
611611
info_message=f"Test assertion error: \
612612
{repr(err)} in {test_func.__name__}",
613613
ending_time=datetime.datetime.now(),
614-
status=str(ExecStatus.Error),
615-
end_status=str(ExecStatus.Fail),
614+
status=ExecStatus.Error.value,
615+
end_status=ExecStatus.Fail.value,
616616
sequence_num=sequence_num,
617617
)
618618
BASE_RESULT.append(base_result)
@@ -624,8 +624,8 @@ def _run_tests(self, tf: str, tst_cls: Any, tst_obj: Any, test_methods: list[str
624624
ending_time=datetime.datetime.now(),
625625
failures=False,
626626
info_message=f"Running {tf}",
627-
status=str(ExecStatus.Success),
628-
end_status=str(ExecStatus.OK),
627+
status=ExecStatus.Success.value,
628+
end_status=ExecStatus.OK.value,
629629
sequence_num=sequence_num,
630630
)
631631
BASE_RESULT.append(base_result)
@@ -727,14 +727,14 @@ def validate_and_run_seed(self, seed_file, session_id: str) -> dict[str, str]:
727727
seed_result = SeedResult(
728728
schema_name=schema,
729729
seed_path=file_name,
730-
status=str(ExecStatus.START),
730+
status=ExecStatus.START.value,
731731
)
732732
SEED_RESULT.append(seed_result)
733733
seed_obj: BaseSeed = self.db_adapter.run_seeds(schema=schema, abs_path=file_path)
734734
seed_result = SeedResult(
735735
schema_name=schema,
736736
seed_path=file_name,
737-
status=str(ExecStatus.COMPLETED),
737+
status=ExecStatus.COMPLETED.value,
738738
)
739739
SEED_RESULT.append(seed_result)
740740
parse_and_fire_seed_report()
@@ -789,14 +789,14 @@ def run_snapshot(self) -> None:
789789
snapshot_result = SnapshotResult(
790790
source_table=obj.source_table_name,
791791
unique_key=obj.unique_key,
792-
status=str(ExecStatus.START),
792+
status=ExecStatus.START.value,
793793
)
794794
SNAPSHOT_RESULT.append(snapshot_result)
795795
self.db_adapter.run_scd(visitran_snapshot=obj)
796796
snapshot_result = SnapshotResult(
797797
source_table=obj.source_table_name,
798798
unique_key=obj.unique_key,
799-
status=str(ExecStatus.COMPLETED),
799+
status=ExecStatus.COMPLETED.value,
800800
)
801801
SNAPSHOT_RESULT.append(snapshot_result)
802802
parse_and_fire_snapshot_report()

frontend/src/ide/editor/no-code-model/no-code-model.jsx

Lines changed: 75 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@ import {
44
Input,
55
Modal,
66
Pagination,
7+
Select,
78
Space,
89
Table,
910
Tabs,
11+
theme,
1012
Tooltip,
1113
Typography,
1214
} from "antd";
13-
import { useEffect, useRef, useState } from "react";
15+
import { useEffect, useMemo, useRef, useState } from "react";
1416
import { Resizable } from "react-resizable";
1517
import Cookies from "js-cookie";
1618
import AnsiToHtml from "ansi-to-html";
@@ -146,7 +148,17 @@ ResizableTitle.propTypes = {
146148
width: PropTypes.number,
147149
};
148150

151+
const LOG_LEVEL_RANK = { debug: 0, info: 1, warn: 2, warning: 2, error: 3 };
152+
const LOG_LEVEL_COLOR = {
153+
debug: "#8c8c8c",
154+
info: undefined,
155+
warn: "#d48806",
156+
warning: "#d48806",
157+
error: "#cf1322",
158+
};
159+
149160
function NoCodeModel({ nodeData }) {
161+
const { token } = theme.useToken();
150162
const axios = useAxiosPrivate();
151163
const csrfToken = Cookies.get("csrftoken");
152164
const sessionId = Cookies.get("sessionid");
@@ -206,7 +218,9 @@ function NoCodeModel({ nodeData }) {
206218
const [seqEdges, setSeqEdges, onSeqEdgesChange] = useEdgesState();
207219
const [reactFlowInstance, setReactFlowInstance] = useState(null);
208220
const [logsInfo, setLogsInfo] = useState([]);
209-
// const logsInfo = [];
221+
const [logsLevel, setLogsLevel] = useState(
222+
() => localStorage.getItem("visitran.logsLevel") || "info"
223+
);
210224
const [reveal, setReveal] = useState(false);
211225
const [seqOrder, setSeqOrder] = useState({});
212226
const [specRevert, setSpecRevert] = useState(false);
@@ -366,6 +380,19 @@ function NoCodeModel({ nodeData }) {
366380
ALLOWED_ATTR: ["style"],
367381
});
368382

383+
const filteredLogs = useMemo(
384+
() =>
385+
logsInfo.filter(
386+
(entry) =>
387+
(LOG_LEVEL_RANK[entry.level] ?? 1) >= (LOG_LEVEL_RANK[logsLevel] ?? 1)
388+
),
389+
[logsInfo, logsLevel]
390+
);
391+
const handleLogsLevelChange = (value) => {
392+
setLogsLevel(value);
393+
localStorage.setItem("visitran.logsLevel", value);
394+
};
395+
369396
const hideGenAIAndTimeTravelTabs = true;
370397
const BOTTOM_TABS = [
371398
{
@@ -643,21 +670,43 @@ function NoCodeModel({ nodeData }) {
643670
),
644671
key: "logs",
645672
children: (
646-
<div
647-
className="logsSection"
648-
style={{
649-
height: `calc(${bottomSectionRef.current.height} - 70px)`,
650-
}}
651-
>
652-
{logsInfo?.map((el, index) => {
653-
return (
673+
<>
674+
<div
675+
style={{
676+
display: "flex",
677+
justifyContent: "flex-end",
678+
padding: "4px 8px",
679+
borderBottom: `1px solid ${token.colorBorderSecondary}`,
680+
}}
681+
>
682+
<Select
683+
size="small"
684+
value={logsLevel}
685+
onChange={handleLogsLevelChange}
686+
style={{ width: 140 }}
687+
options={[
688+
{ value: "debug", label: "All logs" },
689+
{ value: "info", label: "Info & above" },
690+
{ value: "warn", label: "Warnings & errors" },
691+
{ value: "error", label: "Errors only" },
692+
]}
693+
/>
694+
</div>
695+
<div
696+
className="logsSection"
697+
style={{
698+
height: `calc(${bottomSectionRef.current.height} - 100px)`,
699+
}}
700+
>
701+
{filteredLogs.map((el, index) => (
654702
<div
655703
key={index}
656-
dangerouslySetInnerHTML={{ __html: parseLog(el) }}
657-
></div>
658-
);
659-
})}
660-
</div>
704+
style={{ color: LOG_LEVEL_COLOR[el.level] }}
705+
dangerouslySetInnerHTML={{ __html: parseLog(el.message) }}
706+
/>
707+
))}
708+
</div>
709+
</>
661710
),
662711
},
663712
].filter((tab) => {
@@ -1762,28 +1811,32 @@ function NoCodeModel({ nodeData }) {
17621811
};
17631812
const newSocket = io(getBaseUrl(), body);
17641813

1814+
let socketSessionId = null;
17651815
newSocket.on("connect", () => {
17661816
// Listen for the session ID sent by the server
17671817
newSocket.on("session_id", (data) => {
1768-
const sessionId = data.session_id;
1818+
socketSessionId = data.session_id;
17691819
// Listen for messages in the specific room (session ID)
1770-
newSocket.on(`logs:${sessionId}`, (data) => {
1771-
const temp = data?.data?.message;
1820+
newSocket.on(`logs:${socketSessionId}`, (data) => {
1821+
const message = data?.data?.message;
1822+
const level = (data?.data?.level || "info").toLowerCase();
1823+
if (!message) return;
17721824
const doc = document.getElementsByClassName("logsSection");
17731825
if (doc[0]) {
17741826
setTimeout(() => {
17751827
doc[0].scrollTop = doc[0].scrollHeight;
17761828
}, 800);
17771829
}
1778-
setLogsInfo((old) => {
1779-
return [...old, temp];
1780-
});
1830+
setLogsInfo((old) => [...old, { level, message }]);
17811831
});
17821832
});
17831833
});
17841834
return () => {
17851835
// unsubscribe to the channel to stop listening the socket messages for the logId
1786-
newSocket.off(`logs:${sessionId}`);
1836+
if (socketSessionId) {
1837+
newSocket.off(`logs:${socketSessionId}`);
1838+
}
1839+
newSocket.disconnect();
17871840
};
17881841
}, [sessionId]);
17891842

0 commit comments

Comments
 (0)