feat: enhance logging capabilities, implement runtime diagnostics, and update preinstalled skills metadata
This commit is contained in:
160
electron/utils/runtime-diagnostics.ts
Normal file
160
electron/utils/runtime-diagnostics.ts
Normal file
@@ -0,0 +1,160 @@
|
||||
import { app, crashReporter, type BrowserWindow, type WebContents } from 'electron';
|
||||
import path from 'node:path';
|
||||
import logManager from '@electron/service/logger';
|
||||
import { ensureDir, getUserDataDir } from './paths';
|
||||
import { serializeUnknownError, serializeUnknownForLog } from './log-helpers';
|
||||
|
||||
let diagnosticsInstalled = false;
|
||||
let crashReporterStarted = false;
|
||||
|
||||
function describeWebContents(webContents: WebContents): Record<string, unknown> {
|
||||
return {
|
||||
webContentsId: webContents.id,
|
||||
type: webContents.getType(),
|
||||
url: webContents.getURL(),
|
||||
loading: webContents.isLoading(),
|
||||
destroyed: webContents.isDestroyed(),
|
||||
};
|
||||
}
|
||||
|
||||
function describeWindow(window: BrowserWindow): Record<string, unknown> {
|
||||
return {
|
||||
windowId: window.id,
|
||||
title: window.getTitle(),
|
||||
visible: window.isVisible(),
|
||||
minimized: window.isMinimized(),
|
||||
maximized: window.isMaximized(),
|
||||
destroyed: window.isDestroyed(),
|
||||
...describeWebContents(window.webContents),
|
||||
};
|
||||
}
|
||||
|
||||
function getCrashDumpsDirectoryPath(): string {
|
||||
return ensureDir(path.join(getUserDataDir(), 'logs', 'crashDumps'));
|
||||
}
|
||||
|
||||
function attachWindowDiagnostics(window: BrowserWindow): void {
|
||||
logManager.info('BrowserWindow created', describeWindow(window));
|
||||
|
||||
window.on('unresponsive', () => {
|
||||
logManager.warn('BrowserWindow became unresponsive', describeWindow(window));
|
||||
});
|
||||
|
||||
window.on('responsive', () => {
|
||||
logManager.info('BrowserWindow became responsive again', describeWindow(window));
|
||||
});
|
||||
|
||||
window.webContents.on('did-fail-load', (_event, errorCode, errorDescription, validatedURL, isMainFrame, frameProcessId, frameRoutingId) => {
|
||||
logManager.error('WebContents failed to load', {
|
||||
...describeWindow(window),
|
||||
errorCode,
|
||||
errorDescription,
|
||||
validatedURL,
|
||||
isMainFrame,
|
||||
frameProcessId,
|
||||
frameRoutingId,
|
||||
});
|
||||
});
|
||||
|
||||
window.webContents.on('render-process-gone', (_event, details) => {
|
||||
logManager.error('Window render process exited unexpectedly', {
|
||||
...describeWindow(window),
|
||||
reason: details.reason,
|
||||
exitCode: details.exitCode,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function logStartupCheckpoint(stage: string, details: Record<string, unknown> = {}): void {
|
||||
logManager.info(`startup:${stage}`, {
|
||||
pid: process.pid,
|
||||
packaged: app.isPackaged,
|
||||
...details,
|
||||
});
|
||||
}
|
||||
|
||||
export function startLocalCrashReporter(): void {
|
||||
if (crashReporterStarted) {
|
||||
return;
|
||||
}
|
||||
|
||||
const crashDumpsDir = getCrashDumpsDirectoryPath();
|
||||
|
||||
try {
|
||||
app.setPath('crashDumps', crashDumpsDir);
|
||||
} catch (error) {
|
||||
logManager.captureException('Failed to set crashDumps path', error, { crashDumpsDir });
|
||||
}
|
||||
|
||||
try {
|
||||
crashReporter.start({
|
||||
companyName: 'ZhiNian Team',
|
||||
productName: app.getName() || 'NIANXX',
|
||||
submitURL: 'https://127.0.0.1/disabled-crash-upload',
|
||||
uploadToServer: false,
|
||||
compress: true,
|
||||
ignoreSystemCrashHandler: false,
|
||||
rateLimit: false,
|
||||
globalExtra: {
|
||||
appVersion: app.getVersion(),
|
||||
packaged: String(app.isPackaged),
|
||||
platform: process.platform,
|
||||
arch: process.arch,
|
||||
},
|
||||
});
|
||||
|
||||
crashReporterStarted = true;
|
||||
logManager.info('Crash reporter started for local dump collection', {
|
||||
crashDumpsDir,
|
||||
uploadToServer: false,
|
||||
});
|
||||
} catch (error) {
|
||||
logManager.captureException('Failed to start crash reporter', error, { crashDumpsDir });
|
||||
}
|
||||
}
|
||||
|
||||
export function installRuntimeDiagnostics(): void {
|
||||
if (diagnosticsInstalled) {
|
||||
return;
|
||||
}
|
||||
|
||||
diagnosticsInstalled = true;
|
||||
|
||||
process.on('warning', (warning) => {
|
||||
logManager.warn('Node.js process warning', serializeUnknownError(warning));
|
||||
});
|
||||
|
||||
process.on('uncaughtExceptionMonitor', (error) => {
|
||||
logManager.captureException('uncaughtExceptionMonitor', error);
|
||||
});
|
||||
|
||||
process.on('unhandledRejection', (reason) => {
|
||||
logManager.error('unhandledRejection observed by runtime diagnostics', {
|
||||
reason: serializeUnknownForLog(reason),
|
||||
});
|
||||
});
|
||||
|
||||
app.on('browser-window-created', (_event, window) => {
|
||||
attachWindowDiagnostics(window);
|
||||
});
|
||||
|
||||
app.on('render-process-gone', (_event, webContents, details) => {
|
||||
logManager.error('App render process exited unexpectedly', {
|
||||
...describeWebContents(webContents),
|
||||
reason: details.reason,
|
||||
exitCode: details.exitCode,
|
||||
});
|
||||
});
|
||||
|
||||
app.on('child-process-gone', (_event, details) => {
|
||||
logManager.error('Child process exited unexpectedly', serializeUnknownForLog(details));
|
||||
});
|
||||
|
||||
logManager.info('Runtime diagnostics installed', {
|
||||
platform: process.platform,
|
||||
arch: process.arch,
|
||||
pid: process.pid,
|
||||
userDataDir: getUserDataDir(),
|
||||
logsDir: logManager.getLogDirectoryPath(),
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user