Skip to content

Commit 02f4acc

Browse files
committed
Improve trace viewer for large trace logs
1 parent e3642f6 commit 02f4acc

3 files changed

Lines changed: 1004 additions & 330 deletions

File tree

trackio/frontend/src/lib/api.js

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,130 @@ export async function getLogsBatch(project, runs) {
105105
return await callApi("/get_logs_batch", payload);
106106
}
107107

108+
function sqlString(value) {
109+
return `'${String(value ?? "").replaceAll("'", "''")}'`;
110+
}
111+
112+
function scalarLogsFromQueryRows(rows) {
113+
return (rows || []).map((row) => {
114+
const metrics = (() => {
115+
try {
116+
return JSON.parse(row.metrics || "{}");
117+
} catch {
118+
return {};
119+
}
120+
})();
121+
return {
122+
...metrics,
123+
timestamp: row.timestamp,
124+
step: row.step,
125+
};
126+
});
127+
}
128+
129+
function scalarOnlyLogs(logs) {
130+
return (logs || []).map((row) => {
131+
const out = {
132+
timestamp: row.timestamp,
133+
step: row.step,
134+
};
135+
for (const [key, value] of Object.entries(row)) {
136+
if (key === "timestamp" || key === "step") continue;
137+
if (typeof value === "number" && Number.isFinite(value)) out[key] = value;
138+
}
139+
return out;
140+
});
141+
}
142+
143+
async function queryProject(project, query) {
144+
return await callApi("/query_project", { project, query });
145+
}
146+
147+
export async function getTraceStepCounts(project, run) {
148+
if (await isStaticMode()) return [];
149+
150+
const normalized = normalizeRun(run);
151+
const where = normalized.run_id
152+
? `m.run_id = ${sqlString(normalized.run_id)}`
153+
: `m.run_name = ${sqlString(normalized.run)}`;
154+
const result = await queryProject(
155+
project,
156+
`
157+
SELECT
158+
m.step AS step,
159+
SUM(
160+
CASE
161+
WHEN json_type(j.value) = 'array' THEN json_array_length(j.value)
162+
ELSE 1
163+
END
164+
) AS trace_count
165+
FROM metrics m, json_each(CAST(m.metrics AS TEXT)) j
166+
WHERE ${where}
167+
AND m.step IS NOT NULL
168+
AND (
169+
(
170+
json_type(j.value) = 'array'
171+
AND (
172+
json_extract(j.value, '$[0]._type') = 'trackio.trace'
173+
OR json_extract(j.value, '$[0].messages') IS NOT NULL
174+
)
175+
)
176+
OR (
177+
json_type(j.value) = 'object'
178+
AND (
179+
json_extract(j.value, '$._type') = 'trackio.trace'
180+
OR json_extract(j.value, '$.messages') IS NOT NULL
181+
)
182+
)
183+
)
184+
GROUP BY m.step
185+
ORDER BY m.step
186+
`,
187+
);
188+
return (result.rows || []).map((row) => ({
189+
step: Number(row.step),
190+
count: Number(row.trace_count || 0),
191+
})).filter((row) => Number.isFinite(row.step) && row.count > 0);
192+
}
193+
194+
export async function getScalarLogsBatch(project, runs) {
195+
if (await isStaticMode()) {
196+
const out = [];
197+
for (const run of runs) {
198+
const logs = await staticApi.getLogs(project, run);
199+
out.push({ ...normalizeRun(run), logs: scalarOnlyLogs(logs) });
200+
}
201+
return out;
202+
}
203+
204+
const out = [];
205+
for (const run of runs) {
206+
const normalized = normalizeRun(run);
207+
const where = normalized.run_id
208+
? `m.run_id = ${sqlString(normalized.run_id)}`
209+
: `m.run_name = ${sqlString(normalized.run)}`;
210+
const result = await queryProject(
211+
project,
212+
`
213+
SELECT
214+
m.timestamp,
215+
m.step,
216+
json_group_object(j.key, j.value) AS metrics
217+
FROM metrics m, json_each(CAST(m.metrics AS TEXT)) j
218+
WHERE ${where}
219+
AND json_type(j.value) IN ('integer', 'real')
220+
GROUP BY m.id, m.timestamp, m.step
221+
ORDER BY m.timestamp
222+
`,
223+
);
224+
out.push({
225+
...normalized,
226+
logs: scalarLogsFromQueryRows(result.rows),
227+
});
228+
}
229+
return out;
230+
}
231+
108232
export async function getTraces(project, run, options = {}) {
109233
const params = { project, ...normalizeRun(run), ...options };
110234
if (await isStaticMode()) return staticApi.getTraces(project, run, options);

trackio/frontend/src/pages/Metrics.svelte

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import BarPlot from "../components/BarPlot.svelte";
66
import Accordion from "../components/Accordion.svelte";
77
import LoadingTrackio from "../components/LoadingTrackio.svelte";
8-
import { getLogsBatch } from "../lib/api.js";
8+
import { getScalarLogsBatch } from "../lib/api.js";
99
import {
1010
getMetricsPollIntervalMs,
1111
isRateLimitCooldownActive,
@@ -168,7 +168,7 @@
168168
const results = [];
169169
for (let i = 0; i < runs.length; i += MAX_BATCH_RUNS) {
170170
const chunk = runs.slice(i, i + MAX_BATCH_RUNS);
171-
const batch = await getLogsBatch(project, chunk);
171+
const batch = await getScalarLogsBatch(project, chunk);
172172
results.push(...batch);
173173
}
174174
return results;

0 commit comments

Comments
 (0)