From 6838cc54d00a8a55f0e0f555fb324960d681e39b Mon Sep 17 00:00:00 2001 From: Pierre Gironde Date: Sun, 4 Oct 2026 14:45:44 +0200 Subject: [PATCH] better error display --- vscode-appdaemon/src/logExplorer.ts | 21 ++++++++++++++++++- vscode-appdaemon/src/logModel.ts | 12 ++++++++--- vscode-appdaemon/vscode-appdaemon-0.2.0.vsix | Bin 54973 -> 55470 bytes 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/vscode-appdaemon/src/logExplorer.ts b/vscode-appdaemon/src/logExplorer.ts index a2c93e2..97e14c7 100644 --- a/vscode-appdaemon/src/logExplorer.ts +++ b/vscode-appdaemon/src/logExplorer.ts @@ -5,7 +5,7 @@ import { randomBytes } from 'crypto'; import { LogEntry, parseLine, decodeLogLine } from './logModel'; import { findLogSource, SourceDocument } from './logSource'; -type Cursor = { offset: number; identity: string; pending: Buffer; last?: LogEntry; discard: boolean; generation: string }; +type Cursor = { offset: number; identity: string; pending: Buffer; last?: LogEntry; errorBlock?: boolean; discard: boolean; generation: string }; export class LogExplorer implements vscode.Disposable { private panel?: vscode.WebviewPanel; private view?: vscode.WebviewView; @@ -168,6 +168,25 @@ export class LogExplorer implements vscode.Disposable { if (cursor.discard) { cursor.discard = false; continue; } const entry = parseLine(line, file, position, zone); if (entry) { + const isErrorLog = path.basename(file) === 'error.log'; + const failedApp = isErrorLog && entry.app === 'Error' && entry.level === 'ERROR' + ? entry.message.match(/^=+\s+Failed to start '([^']+)'/) + : undefined; + if (failedApp) { + entry.app = failedApp[1]; + entry.message = `Failed to start '${failedApp[1]}'`; + cursor.errorBlock = true; + } else if (isErrorLog && cursor.errorBlock && cursor.last && entry.app === 'Error' && entry.level === 'ERROR') { + if ((cursor.last.raw?.length || 0) < 64 * 1024) { + cursor.last.message += '\n' + entry.message; + cursor.last.raw += '\n' + line; + } + if (/^=+\s*$/.test(entry.message)) { cursor.errorBlock = false; } + changed = true; + continue; + } else if (cursor.errorBlock) { + cursor.errorBlock = false; + } if (!entry.trace_id && !line.startsWith('{')) { entry.event_id += ':' + cursor.generation; } const existing = this.entries.get(entry.event_id); if (!existing || entry.raw?.startsWith('{')) { this.entries.set(entry.event_id, entry); } diff --git a/vscode-appdaemon/src/logModel.ts b/vscode-appdaemon/src/logModel.ts index 1b1eb11..a33ead5 100644 --- a/vscode-appdaemon/src/logModel.ts +++ b/vscode-appdaemon/src/logModel.ts @@ -14,9 +14,15 @@ export function rawTime(date: string, zone: string): number { const cacheKey = zone + ':' + date; const cached = timeCache.get(cacheKey); if (cached !== undefined) { return cached; } - const m = date.match(/^(\d\d)-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)/); + const m = date.match(/^(?:(\d{4})-(\d\d)-(\d\d)|(\d\d)-(\d\d)-(\d\d)) (\d\d):(\d\d):(\d\d)/); if (!m) { return NaN; } - const utc = Date.UTC(2000 + Number(m[3]), Number(m[2]) - 1, Number(m[1]), Number(m[4]), Number(m[5]), Number(m[6])); + const year = Number(m[1] || (2000 + Number(m[6]))); + const month = Number(m[2] || m[5]); + const day = Number(m[3] || m[4]); + const hour = Number(m[7]); + const minute = Number(m[8]); + const second = Number(m[9]); + const utc = Date.UTC(year, month - 1, day, hour, minute, second); try { let formatter = formatters.get(zone); if (!formatter) { formatter = new Intl.DateTimeFormat('en-GB', { timeZone: zone, year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', hourCycle: 'h23' }); formatters.set(zone, formatter); } @@ -36,7 +42,7 @@ export function parseLine(line: string, file: string, offset: number, zone = 'Eu return { ...e, level: String(e.level || 'INFO'), kind: String(e.kind || 'log'), file, raw: line, time: Date.parse(e.timestamp) }; } catch { return undefined; } } - const m = line.match(/^(\d{2}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(?:\.\d+)?)\s+(DEBUG|INFO|WARNING|ERROR|CRITICAL)\s+([^:]+):\s?(.*)$/); + const m = line.match(/^((?:\d{2}-\d{2}-\d{2}|\d{4}-\d{2}-\d{2}) \d{2}:\d{2}:\d{2}(?:\.\d+)?)\s+(DEBUG|INFO|WARNING|ERROR|CRITICAL)\s+([^:]+):\s?(.*)$/); if (!m) { return undefined; } const group = m[4].match(/\[group=([^\]\r\n]+):([a-f0-9]{12})\]/i); const id = m[4].match(/\[event=([\w-]+) trace=([\w-]+)\]/); diff --git a/vscode-appdaemon/vscode-appdaemon-0.2.0.vsix b/vscode-appdaemon/vscode-appdaemon-0.2.0.vsix index cbdadbf4c7e24bc1de71411e75428fab69ec80c0..4332377af78041b7a8cea108fd5d5af94e92a036 100644 GIT binary patch delta 11281 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 ZrqEs`h76lZz8)h^PFcl<{WANZ`+t{+SdIVy