feat: clean up collection job log UI
- Filter out debug noise (plan-B per-path lines, heartbeats, timing stats, telemetry) - Strip server-side nanosecond timestamp prefix from displayed messages - Transform snapshot progress lines to show current path instead of doc count + ETA - Humanize recurring message patterns (plan-B summary, prefetch, snapshot total) - Raw collect.log and raw_export.json are unaffected Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -523,14 +523,61 @@ function appendJobLog(message) {
|
||||
return;
|
||||
}
|
||||
|
||||
const time = new Date().toLocaleTimeString('ru-RU', { hour12: false });
|
||||
const parsed = parseServerLogLine(message);
|
||||
if (isCollectLogNoise(parsed.message)) {
|
||||
// Still count toward log length so syncServerLogs offset stays correct,
|
||||
// but mark as hidden so renderCollectionJob skips it.
|
||||
collectionJob.logs.push({
|
||||
id: ++collectionJobLogCounter,
|
||||
time: parsed.time || new Date().toLocaleTimeString('ru-RU', { hour12: false }),
|
||||
message: parsed.message,
|
||||
hidden: true
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
collectionJob.logs.push({
|
||||
id: ++collectionJobLogCounter,
|
||||
time,
|
||||
message
|
||||
time: parsed.time || new Date().toLocaleTimeString('ru-RU', { hour12: false }),
|
||||
message: humanizeCollectLogMessage(parsed.message)
|
||||
});
|
||||
}
|
||||
|
||||
// Transform technical log messages into human-readable form for the UI.
|
||||
// The original messages are preserved in collect.log / raw_export.
|
||||
function humanizeCollectLogMessage(msg) {
|
||||
// "Redfish snapshot: документов=520, ETA≈16s, корни=Chassis(294), Systems(114), последний=/redfish/v1/..."
|
||||
// → "Snapshot: /Chassis/Self/PCIeDevices/00_34_04"
|
||||
let m = msg.match(/snapshot:\s+документов=\d+[^,]*,.*последний=(\S+)/i);
|
||||
if (m) {
|
||||
const path = m[1].replace(/^\.\.\./, '').replace(/^\/redfish\/v1/, '') || m[1];
|
||||
return `Snapshot: ${path}`;
|
||||
}
|
||||
|
||||
// "Redfish snapshot: собрано N документов"
|
||||
m = msg.match(/snapshot:\s+собрано\s+(\d+)\s+документов/i);
|
||||
if (m) {
|
||||
return `Snapshot: итого ${m[1]} документов`;
|
||||
}
|
||||
|
||||
// "Redfish: plan-B завершен за 30s (targets=18, recovered=0)"
|
||||
m = msg.match(/plan-B завершен за ([^(]+)\(targets=(\d+),\s*recovered=(\d+)\)/i);
|
||||
if (m) {
|
||||
const recovered = parseInt(m[3], 10);
|
||||
const suffix = recovered > 0 ? `, восстановлено ${m[3]}` : '';
|
||||
return `Plan-B: завершен за ${m[1].trim()}${suffix}`;
|
||||
}
|
||||
|
||||
// "Redfish: prefetch критичных endpoint (адаптивно 9/72)..."
|
||||
m = msg.match(/prefetch критичных endpoint[^(]*\(([^)]+)\)/i);
|
||||
if (m) {
|
||||
return `Prefetch критичных endpoint (${m[1]})`;
|
||||
}
|
||||
|
||||
// Strip "Redfish: " / "Redfish snapshot: " prefix — redundant in context
|
||||
return msg.replace(/^Redfish(?:\s+snapshot)?:\s+/i, '');
|
||||
}
|
||||
|
||||
function renderCollectionJob() {
|
||||
const jobStatusBlock = document.getElementById('api-job-status');
|
||||
const jobIdValue = document.getElementById('job-id-value');
|
||||
@@ -576,9 +623,11 @@ function renderCollectionJob() {
|
||||
renderJobActiveModules(activeModulesBlock, activeModulesList);
|
||||
renderJobDebugInfo(debugInfoBlock, debugSummary, phaseTelemetryNode);
|
||||
|
||||
logsList.innerHTML = [...collectionJob.logs].reverse().map((log) => (
|
||||
`<li><span class="log-time">${escapeHtml(log.time)}</span><span class="log-message">${escapeHtml(log.message)}</span></li>`
|
||||
)).join('');
|
||||
logsList.innerHTML = [...collectionJob.logs].reverse()
|
||||
.filter((log) => !log.hidden)
|
||||
.map((log) => (
|
||||
`<li><span class="log-time">${escapeHtml(log.time)}</span><span class="log-message">${escapeHtml(log.message)}</span></li>`
|
||||
)).join('');
|
||||
|
||||
cancelButton.disabled = isTerminal;
|
||||
setApiFormBlocked(!isTerminal);
|
||||
@@ -668,6 +717,38 @@ function syncServerLogs(logs) {
|
||||
}
|
||||
}
|
||||
|
||||
// Patterns for log lines that are internal debug noise and should not be shown in the UI.
|
||||
const _collectLogNoisePatterns = [
|
||||
/plan-B \(\d+\/\d+/, // individual plan-B step lines
|
||||
/plan-B топ веток/,
|
||||
/snapshot: heartbeat/,
|
||||
/snapshot: post-probe коллекций \(/,
|
||||
/snapshot: топ веток/,
|
||||
/prefetch завершен/,
|
||||
/cooldown перед повторным добором/,
|
||||
/Redfish telemetry:/,
|
||||
/redfish-postprobe-metrics:/,
|
||||
/redfish-prefetch-metrics:/,
|
||||
/redfish-collect:/,
|
||||
/redfish-profile-plan:/,
|
||||
/redfish replay:/,
|
||||
];
|
||||
|
||||
function isCollectLogNoise(message) {
|
||||
return _collectLogNoisePatterns.some((re) => re.test(message));
|
||||
}
|
||||
|
||||
// Strip the server-side RFC3339Nano timestamp prefix from a log line and return {time, message}.
|
||||
function parseServerLogLine(raw) {
|
||||
const m = String(raw).match(/^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z)\s+(.*)/s);
|
||||
if (!m) {
|
||||
return { time: null, message: String(raw).trim() };
|
||||
}
|
||||
const d = new Date(m[1]);
|
||||
const time = isNaN(d) ? null : d.toLocaleTimeString('ru-RU', { hour12: false });
|
||||
return { time, message: m[2].trim() };
|
||||
}
|
||||
|
||||
function normalizeJobStatus(status) {
|
||||
return String(status || '').trim().toLowerCase();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user