Build cross-platform desktop apps with JavaScript, HTML, and CSS
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Development tools, debugging utilities, crash reporting, and performance monitoring for building and maintaining Electron applications.
Crash reporting system for collecting and submitting crash information.
/**
* Submit crash reports to a remote server
*/
interface CrashReporter {
/** Starts crash reporting */
start(options: CrashReporterStartOptions): void;
/** Returns the last crash report */
getLastCrashReport(): CrashReport | null;
/** Returns all uploaded crash reports */
getUploadedReports(): CrashReport[];
/** Returns the date and ID of the last crash report */
getUploadToServer(): boolean;
/** Sets extra key-value pairs that will be sent along with crash reports */
addExtraParameter(key: string, value: string): void;
/** Removes an extra parameter that was set with addExtraParameter */
removeExtraParameter(key: string): void;
/** Returns an object with all extra parameters that will be sent with crash reports */
getParameters(): Record<string, string>;
}
declare const crashReporter: CrashReporter;Usage Examples:
const { crashReporter, app } = require('electron');
// Setup crash reporting
function setupCrashReporting() {
crashReporter.start({
productName: 'MyElectronApp',
companyName: 'MyCompany',
submitURL: 'https://crashes.myapp.com/submit',
uploadToServer: true,
ignoreSystemCrashHandler: false,
rateLimit: true,
compress: true,
extra: {
'app-version': app.getVersion(),
'platform': process.platform,
'user-id': getUserId(),
'session-id': getSessionId()
}
});
console.log('Crash reporting initialized');
}
// Add runtime information
function addCrashMetadata() {
crashReporter.addExtraParameter('memory-usage', process.memoryUsage().heapUsed.toString());
crashReporter.addExtraParameter('uptime', process.uptime().toString());
crashReporter.addExtraParameter('node-version', process.version);
crashReporter.addExtraParameter('electron-version', process.versions.electron);
}
// Get crash reports
function getCrashReports() {
const lastReport = crashReporter.getLastCrashReport();
if (lastReport) {
console.log('Last crash:', lastReport);
}
const uploadedReports = crashReporter.getUploadedReports();
console.log('Uploaded crash reports:', uploadedReports.length);
return { lastReport, uploadedReports };
}
// Simulate crash for testing
function simulateCrash() {
if (process.env.NODE_ENV === 'development') {
process.crash();
}
}Performance tracing and profiling for analyzing application performance.
/**
* Collect tracing data from Chromium's content module for finding performance bottlenecks and slow operations
*/
interface ContentTracing {
/** Get a set of category groups */
getCategories(): Promise<string[]>;
/** Start recording on all processes */
startRecording(options: TraceCategoriesAndOptions | TraceConfig): Promise<void>;
/** Stop recording on all processes */
stopRecording(resultFilePath?: string): Promise<string>;
/** Start monitoring on all processes */
startMonitoring(options: TraceCategoriesAndOptions): Promise<void>;
/** Stop monitoring on all processes */
stopMonitoring(): Promise<void>;
/** Capture monitoring snapshot on all processes */
captureMonitoringSnapshot(resultFilePath?: string): Promise<string>;
/** Get the maximum usage across processes of trace buffer as a percentage of the full state */
getTraceBufferUsage(): Promise<TraceBufferUsageReturnValue>;
}
declare const contentTracing: ContentTracing;Usage Examples:
const { contentTracing } = require('electron');
const fs = require('fs');
const path = require('path');
// Performance profiling
async function startPerformanceProfiling() {
try {
// Get available categories
const categories = await contentTracing.getCategories();
console.log('Available trace categories:', categories);
// Start recording with specific categories
await contentTracing.startRecording({
categoryFilter: 'blink,cc,gpu,renderer.scheduler,v8,disabled-by-default-devtools.timeline',
traceOptions: 'record-continuously,enable-sampling,enable-systrace'
});
console.log('Performance tracing started');
// Run for 10 seconds then stop
setTimeout(async () => {
await stopPerformanceProfiling();
}, 10000);
} catch (error) {
console.error('Failed to start tracing:', error);
}
}
async function stopPerformanceProfiling() {
try {
const tracePath = path.join(__dirname, 'traces', `trace-${Date.now()}.json`);
const resultPath = await contentTracing.stopRecording(tracePath);
console.log('Performance trace saved to:', resultPath);
// Analyze trace file
analyzePerfTrace(resultPath);
} catch (error) {
console.error('Failed to stop tracing:', error);
}
}
// Memory monitoring
async function monitorMemoryUsage() {
try {
await contentTracing.startMonitoring({
categoryFilter: 'blink.console,disabled-by-default-memory-infra',
traceOptions: 'record-continuously'
});
console.log('Memory monitoring started');
// Capture snapshots every 5 seconds
const interval = setInterval(async () => {
try {
const snapshotPath = path.join(__dirname, 'memory-snapshots', `memory-${Date.now()}.json`);
await contentTracing.captureMonitoringSnapshot(snapshotPath);
console.log('Memory snapshot saved:', snapshotPath);
} catch (error) {
console.error('Failed to capture memory snapshot:', error);
}
}, 5000);
// Stop after 1 minute
setTimeout(async () => {
clearInterval(interval);
await contentTracing.stopMonitoring();
console.log('Memory monitoring stopped');
}, 60000);
} catch (error) {
console.error('Failed to start memory monitoring:', error);
}
}
// Check trace buffer usage
async function checkTraceBufferUsage() {
try {
const usage = await contentTracing.getTraceBufferUsage();
console.log('Trace buffer usage:', usage);
if (usage.percentFull > 80) {
console.warn('Trace buffer is getting full!');
}
return usage;
} catch (error) {
console.error('Failed to get trace buffer usage:', error);
return null;
}
}
// Analyze performance trace
function analyzePerfTrace(tracePath) {
try {
const traceData = JSON.parse(fs.readFileSync(tracePath, 'utf8'));
// Basic analysis
const events = traceData.traceEvents || [];
const totalEvents = events.length;
const timespan = getTraceTimespan(events);
console.log(`Trace analysis:
- Total events: ${totalEvents}
- Timespan: ${timespan}ms
- Categories: ${getUniqueCategories(events).join(', ')}`);
// Find slow operations
const slowOperations = findSlowOperations(events);
if (slowOperations.length > 0) {
console.log('Slow operations detected:', slowOperations);
}
} catch (error) {
console.error('Failed to analyze trace:', error);
}
}
function getTraceTimespan(events) {
if (events.length === 0) return 0;
const timestamps = events.map(e => e.ts).filter(ts => ts);
return (Math.max(...timestamps) - Math.min(...timestamps)) / 1000;
}
function getUniqueCategories(events) {
const categories = new Set();
events.forEach(event => {
if (event.cat) {
event.cat.split(',').forEach(cat => categories.add(cat.trim()));
}
});
return Array.from(categories);
}
function findSlowOperations(events, threshold = 16) {
return events
.filter(event => event.dur && event.dur > threshold * 1000) // Convert to microseconds
.map(event => ({
name: event.name,
category: event.cat,
duration: event.dur / 1000, // Convert to milliseconds
timestamp: event.ts
}))
.sort((a, b) => b.duration - a.duration)
.slice(0, 10); // Top 10 slowest operations
}Network logging for debugging network issues.
/**
* Logging network events for a session
*/
interface NetLog {
/** Starts recording network events to path */
startLogging(path: string, options?: NetLogStartLoggingOptions): Promise<void>;
/** Stops recording network events */
stopLogging(): Promise<void>;
/** Returns whether there is currently a network log being recorded */
currentlyLogging: boolean;
/** A string property that returns the path to the current log file */
currentlyLoggingPath: string;
}Usage Examples:
const { session } = require('electron');
const path = require('path');
// Network debugging
async function startNetworkLogging() {
const netLog = session.defaultSession.netLog;
if (netLog.currentlyLogging) {
console.log('Network logging already active');
return;
}
try {
const logPath = path.join(__dirname, 'logs', `network-${Date.now()}.json`);
await netLog.startLogging(logPath, {
captureMode: 'everything',
maxFileSize: 100 * 1024 * 1024 // 100MB
});
console.log('Network logging started:', logPath);
// Stop after 5 minutes
setTimeout(async () => {
await stopNetworkLogging();
}, 5 * 60 * 1000);
} catch (error) {
console.error('Failed to start network logging:', error);
}
}
async function stopNetworkLogging() {
const netLog = session.defaultSession.netLog;
if (!netLog.currentlyLogging) {
console.log('Network logging not active');
return;
}
try {
await netLog.stopLogging();
console.log('Network logging stopped');
} catch (error) {
console.error('Failed to stop network logging:', error);
}
}
// Debug specific network issues
function debugNetworkIssues() {
const { webContents } = require('electron');
// Monitor all webContents for network events
webContents.getAllWebContents().forEach(contents => {
contents.on('did-fail-load', (event, errorCode, errorDescription, validatedURL) => {
console.error('Failed to load:', {
url: validatedURL,
error: errorCode,
description: errorDescription
});
});
contents.on('did-fail-provisional-load', (event, errorCode, errorDescription, validatedURL) => {
console.error('Provisional load failed:', {
url: validatedURL,
error: errorCode,
description: errorDescription
});
});
contents.on('certificate-error', (event, url, error, certificate) => {
console.error('Certificate error:', {
url: url,
error: error,
issuer: certificate.issuer
});
});
});
}Chrome DevTools debugging interface.
/**
* An alternate transport for Chrome's remote debugging protocol
*/
interface Debugger extends EventEmitter {
/** Attaches the debugger to the webContents */
attach(protocolVersion?: string): void;
/** Returns whether a debugger is attached to the webContents */
isAttached(): boolean;
/** Detaches the debugger from the webContents */
detach(): void;
/** Send given command to the debugging target */
sendCommand(method: string, commandParams?: any): Promise<any>;
}Usage Examples:
const { BrowserWindow } = require('electron');
// Debug a specific window
function setupDebugger(window) {
const debugger = window.webContents.debugger;
try {
debugger.attach('1.3');
console.log('Debugger attached');
} catch (error) {
console.error('Failed to attach debugger:', error);
return;
}
// Handle debugger events
debugger.on('detach', (event, reason) => {
console.log('Debugger detached:', reason);
});
debugger.on('message', (event, method, params) => {
if (method === 'Network.responseReceived') {
console.log('Network response:', params);
}
});
// Enable network domain
debugger.sendCommand('Network.enable').then(() => {
console.log('Network domain enabled');
});
// Enable runtime domain
debugger.sendCommand('Runtime.enable').then(() => {
console.log('Runtime domain enabled');
});
return debugger;
}
// CPU profiling
async function profileCPU(window, duration = 10000) {
const debugger = window.webContents.debugger;
try {
if (!debugger.isAttached()) {
debugger.attach('1.3');
}
// Enable profiler
await debugger.sendCommand('Profiler.enable');
// Start profiling
await debugger.sendCommand('Profiler.start');
console.log('CPU profiling started');
// Stop after specified duration
setTimeout(async () => {
try {
const profile = await debugger.sendCommand('Profiler.stop');
// Save profile
const fs = require('fs');
const profilePath = `cpu-profile-${Date.now()}.json`;
fs.writeFileSync(profilePath, JSON.stringify(profile.profile, null, 2));
console.log('CPU profile saved:', profilePath);
} catch (error) {
console.error('Failed to stop profiling:', error);
}
}, duration);
} catch (error) {
console.error('Failed to start CPU profiling:', error);
}
}
// Memory heap snapshot
async function takeHeapSnapshot(window) {
const debugger = window.webContents.debugger;
try {
if (!debugger.isAttached()) {
debugger.attach('1.3');
}
// Enable heap profiler
await debugger.sendCommand('HeapProfiler.enable');
// Take snapshot
await debugger.sendCommand('HeapProfiler.takeHeapSnapshot');
console.log('Heap snapshot taken');
// Listen for snapshot data
debugger.on('message', (event, method, params) => {
if (method === 'HeapProfiler.addHeapSnapshotChunk') {
// Process snapshot chunk
console.log('Received heap snapshot chunk');
}
});
} catch (error) {
console.error('Failed to take heap snapshot:', error);
}
}interface CrashReporterStartOptions {
productName: string;
companyName: string;
submitURL: string;
uploadToServer?: boolean;
ignoreSystemCrashHandler?: boolean;
rateLimit?: boolean;
compress?: boolean;
extra?: Record<string, string>;
crashesDirectory?: string;
}
interface CrashReport {
id: string;
date: Date;
productName: string;
productVersion: string;
process: string;
reason: string;
extra: Record<string, string>;
}
interface TraceCategoriesAndOptions {
categoryFilter: string;
traceOptions: string;
}
interface TraceConfig {
recording_mode?: 'record-until-full' | 'record-continuously' | 'record-as-much-as-possible' | 'trace-to-console';
enable_sampling?: boolean;
enable_systrace?: boolean;
enable_argument_filter?: boolean;
included_categories?: string[];
excluded_categories?: string[];
synthetic_delays?: string[];
memory_dump_config?: Record<string, any>;
}
interface TraceBufferUsageReturnValue {
value: number;
percentage: number;
percentFull: number;
}
interface NetLogStartLoggingOptions {
captureMode?: 'default' | 'includeCookiesAndCredentials' | 'includeSocketBytes' | 'everything';
maxFileSize?: number;
}Install with Tessl CLI
npx tessl i tessl/npm-electron