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;
|
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({
|
collectionJob.logs.push({
|
||||||
id: ++collectionJobLogCounter,
|
id: ++collectionJobLogCounter,
|
||||||
time,
|
time: parsed.time || new Date().toLocaleTimeString('ru-RU', { hour12: false }),
|
||||||
message
|
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() {
|
function renderCollectionJob() {
|
||||||
const jobStatusBlock = document.getElementById('api-job-status');
|
const jobStatusBlock = document.getElementById('api-job-status');
|
||||||
const jobIdValue = document.getElementById('job-id-value');
|
const jobIdValue = document.getElementById('job-id-value');
|
||||||
@@ -576,9 +623,11 @@ function renderCollectionJob() {
|
|||||||
renderJobActiveModules(activeModulesBlock, activeModulesList);
|
renderJobActiveModules(activeModulesBlock, activeModulesList);
|
||||||
renderJobDebugInfo(debugInfoBlock, debugSummary, phaseTelemetryNode);
|
renderJobDebugInfo(debugInfoBlock, debugSummary, phaseTelemetryNode);
|
||||||
|
|
||||||
logsList.innerHTML = [...collectionJob.logs].reverse().map((log) => (
|
logsList.innerHTML = [...collectionJob.logs].reverse()
|
||||||
`<li><span class="log-time">${escapeHtml(log.time)}</span><span class="log-message">${escapeHtml(log.message)}</span></li>`
|
.filter((log) => !log.hidden)
|
||||||
)).join('');
|
.map((log) => (
|
||||||
|
`<li><span class="log-time">${escapeHtml(log.time)}</span><span class="log-message">${escapeHtml(log.message)}</span></li>`
|
||||||
|
)).join('');
|
||||||
|
|
||||||
cancelButton.disabled = isTerminal;
|
cancelButton.disabled = isTerminal;
|
||||||
setApiFormBlocked(!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) {
|
function normalizeJobStatus(status) {
|
||||||
return String(status || '').trim().toLowerCase();
|
return String(status || '').trim().toLowerCase();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user