Skip to content

Commit 0b3169a

Browse files
committed
fix(chat): persist packaged renderer diagnostics
1 parent 498e190 commit 0b3169a

File tree

5 files changed

+187
-0
lines changed

5 files changed

+187
-0
lines changed

desktop/src/main.cjs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1359,6 +1359,34 @@ function getRendererConsoleLogPath() {
13591359
}
13601360
}
13611361

1362+
function getRendererDebugLogPath() {
1363+
try {
1364+
const dir = app.getPath("userData");
1365+
fs.mkdirSync(dir, { recursive: true });
1366+
return path.join(dir, "renderer-debug.log");
1367+
} catch {
1368+
return null;
1369+
}
1370+
}
1371+
1372+
function appendRendererDebugLog(line) {
1373+
const logPath = getRendererDebugLogPath();
1374+
if (!logPath) return;
1375+
try {
1376+
fs.appendFileSync(logPath, line, { encoding: "utf8" });
1377+
} catch {}
1378+
}
1379+
1380+
function stringifyDebugDetails(details) {
1381+
if (details == null) return "";
1382+
if (typeof details === "string") return details;
1383+
try {
1384+
return JSON.stringify(details);
1385+
} catch (err) {
1386+
return `[unserializable:${err?.message || err}]`;
1387+
}
1388+
}
1389+
13621390
function setupRendererConsoleLogging(win) {
13631391
if (!debugEnabled()) return;
13641392

@@ -1575,6 +1603,15 @@ function registerWindowIpc() {
15751603
}
15761604
});
15771605

1606+
ipcMain.on("debug:log", (event, payload) => {
1607+
const scope = String(payload?.scope || "renderer").trim() || "renderer";
1608+
const message = String(payload?.message || "").trim() || "(empty)";
1609+
const url = String(payload?.url || event?.sender?.getURL?.() || "").trim();
1610+
const details = stringifyDebugDetails(payload?.details);
1611+
const suffix = details ? ` details=${details}` : "";
1612+
appendRendererDebugLog(`[${nowIso()}] [${scope}] ${message} url=${url}${suffix}\n`);
1613+
});
1614+
15781615
ipcMain.handle("app:setCloseBehavior", (_event, behavior) => {
15791616
try {
15801617
const next = setCloseBehavior(behavior);

desktop/src/preload.cjs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,65 @@
11
const { contextBridge, ipcRenderer } = require("electron");
22

3+
function sendDebugLog(scope, message, details) {
4+
try {
5+
ipcRenderer.send("debug:log", {
6+
scope: String(scope || "renderer"),
7+
message: String(message || ""),
8+
details: details == null ? {} : details,
9+
url: typeof location !== "undefined" ? String(location.href || "") : "",
10+
});
11+
} catch {}
12+
}
13+
14+
sendDebugLog("preload", "script-start", {
15+
userAgent: typeof navigator !== "undefined" ? String(navigator.userAgent || "") : "",
16+
});
17+
18+
if (typeof document !== "undefined") {
19+
document.addEventListener("readystatechange", () => {
20+
sendDebugLog("preload", "document-readystate", {
21+
readyState: String(document.readyState || ""),
22+
});
23+
});
24+
}
25+
26+
if (typeof window !== "undefined") {
27+
window.addEventListener("DOMContentLoaded", () => {
28+
sendDebugLog("preload", "dom-content-loaded");
29+
});
30+
31+
window.addEventListener("load", () => {
32+
sendDebugLog("preload", "window-load");
33+
});
34+
35+
window.addEventListener("error", (event) => {
36+
sendDebugLog("preload", "window-error", {
37+
message: String(event?.message || ""),
38+
filename: String(event?.filename || ""),
39+
lineno: Number(event?.lineno || 0),
40+
colno: Number(event?.colno || 0),
41+
});
42+
});
43+
44+
window.addEventListener("unhandledrejection", (event) => {
45+
const reason = event?.reason;
46+
sendDebugLog("preload", "window-unhandledrejection", {
47+
reason:
48+
reason instanceof Error
49+
? {
50+
name: String(reason.name || "Error"),
51+
message: String(reason.message || ""),
52+
stack: String(reason.stack || ""),
53+
}
54+
: String(reason || ""),
55+
});
56+
});
57+
58+
window.setTimeout(() => {
59+
sendDebugLog("preload", "set-timeout-0");
60+
}, 0);
61+
}
62+
363
contextBridge.exposeInMainWorld("wechatDesktop", {
464
// Marker used by the frontend to distinguish the Electron desktop shell from the pure web build.
565
__brand: "WeChatDataAnalysisDesktop",
@@ -8,6 +68,7 @@ contextBridge.exposeInMainWorld("wechatDesktop", {
868
close: () => ipcRenderer.invoke("window:close"),
969
isMaximized: () => ipcRenderer.invoke("window:isMaximized"),
1070
isDebugEnabled: () => ipcRenderer.invoke("app:isDebugEnabled"),
71+
logDebug: (scope, message, details = {}) => sendDebugLog(scope, message, details),
1172

1273
getAutoLaunch: () => ipcRenderer.invoke("app:getAutoLaunch"),
1374
setAutoLaunch: (enabled) => ipcRenderer.invoke("app:setAutoLaunch", !!enabled),

frontend/composables/chat/useChatMessages.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ export const useChatMessages = ({
3636

3737
const logMessagePhase = (phase, details = {}) => {
3838
if (!isDesktopRenderer()) return
39+
try {
40+
window.wechatDesktop?.logDebug?.('chat-messages', phase, details)
41+
} catch {}
3942
console.info(`[chat-messages] ${phase}`, {
4043
account: String(selectedAccount.value || '').trim(),
4144
selectedUsername: String(selectedContact.value?.username || '').trim(),

frontend/pages/chat/[[username]].vue

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ const shouldLogChatBootstrap = () => isDesktopShell() || desktopDebugEnabled.val
9494
9595
const logChatBootstrap = (phase, details = {}) => {
9696
if (!shouldLogChatBootstrap()) return
97+
try {
98+
window.wechatDesktop?.logDebug?.('chat-bootstrap', phase, details)
99+
} catch {}
97100
console.info(`[chat-bootstrap] ${phase}`, {
98101
elapsedMs: chatBootstrapElapsedMs(),
99102
route: route.fullPath,
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
const isDesktopShell = () => {
2+
if (typeof window === 'undefined') return false
3+
return !!window.wechatDesktop?.__brand
4+
}
5+
6+
const formatError = (error) => {
7+
if (!error) return ''
8+
if (error instanceof Error) {
9+
return {
10+
name: String(error.name || 'Error'),
11+
message: String(error.message || ''),
12+
stack: String(error.stack || '')
13+
}
14+
}
15+
if (typeof error === 'object') {
16+
try {
17+
return JSON.parse(JSON.stringify(error))
18+
} catch {}
19+
}
20+
return String(error)
21+
}
22+
23+
const logDesktopDebug = (phase, details = {}) => {
24+
if (!isDesktopShell()) return
25+
try {
26+
window.wechatDesktop?.logDebug?.('nuxt-bootstrap', phase, {
27+
href: String(window.location?.href || ''),
28+
...details
29+
})
30+
} catch {}
31+
try {
32+
console.info(`[nuxt-bootstrap] ${phase}`, details)
33+
} catch {}
34+
}
35+
36+
export default defineNuxtPlugin((nuxtApp) => {
37+
logDesktopDebug('plugin:setup')
38+
39+
if (typeof window !== 'undefined') {
40+
window.addEventListener('error', (event) => {
41+
logDesktopDebug('window:error', {
42+
message: String(event?.message || ''),
43+
filename: String(event?.filename || ''),
44+
lineno: Number(event?.lineno || 0),
45+
colno: Number(event?.colno || 0),
46+
error: formatError(event?.error)
47+
})
48+
})
49+
50+
window.addEventListener('unhandledrejection', (event) => {
51+
logDesktopDebug('window:unhandledrejection', {
52+
reason: formatError(event?.reason)
53+
})
54+
})
55+
}
56+
57+
nuxtApp.hook('app:created', () => {
58+
logDesktopDebug('app:created')
59+
})
60+
61+
nuxtApp.hook('app:beforeMount', () => {
62+
logDesktopDebug('app:beforeMount')
63+
})
64+
65+
nuxtApp.hook('app:mounted', () => {
66+
logDesktopDebug('app:mounted')
67+
})
68+
69+
nuxtApp.hook('page:start', () => {
70+
logDesktopDebug('page:start')
71+
})
72+
73+
nuxtApp.hook('page:finish', () => {
74+
logDesktopDebug('page:finish')
75+
})
76+
77+
nuxtApp.hook('vue:error', (error, _instance, info) => {
78+
logDesktopDebug('vue:error', {
79+
info: String(info || ''),
80+
error: formatError(error)
81+
})
82+
})
83+
})

0 commit comments

Comments
 (0)