Electron日志管理:应用运行日志记录与分析
概述
在Electron应用开发过程中,有效的日志管理是确保应用稳定性和可维护性的关键。Electron提供了多种日志记录机制,从简单的控制台输出到高级的网络日志记录,帮助开发者监控应用运行状态、调试问题并分析性能瓶颈。
内置日志机制
1. 控制台日志
最基本的日志方式是通过console对象:
// 主进程日志
console.log('主进程启动');
console.error('发生错误');
console.warn('警告信息');
// 渲染进程日志
console.log('渲染进程初始化完成');
2. 启用详细日志记录
Electron支持通过命令行参数启用详细日志:
# 启用标准错误输出日志
electron --enable-logging
# 将日志输出到文件
electron --enable-logging=file
# 指定日志文件路径
electron --enable-logging --log-file=/path/to/log.txt
# 设置日志级别
electron --enable-logging --log-level=1
3. 环境变量控制
# 通过环境变量启用日志
ELECTRON_ENABLE_LOGGING=1 electron
# 指定日志文件
ELECTRON_ENABLE_LOGGING=1 ELECTRON_LOG_FILE=/path/to/log.txt electron
网络日志记录
Electron提供了专门的NetLog API用于记录网络活动:
const { session } = require('electron');
// 创建网络日志实例
const netLog = session.defaultSession.netLog;
// 开始记录网络日志
async function startNetLogging() {
try {
await netLog.startLogging('/path/to/netlog.json', {
captureMode: 'default', // default, includeSensitive, everything
maxFileSize: 10485760 // 10MB
});
console.log('网络日志记录已启动');
} catch (error) {
console.error('启动网络日志失败:', error);
}
}
// 停止记录
async function stopNetLogging() {
try {
await netLog.stopLogging();
console.log('网络日志记录已停止');
} catch (error) {
console.error('停止网络日志失败:', error);
}
}
// 检查当前是否在记录
console.log('正在记录网络日志:', netLog.currentlyLogging);
多进程日志管理
主进程日志
const { app } = require('electron');
const fs = require('fs');
const path = require('path');
class MainProcessLogger {
constructor() {
this.logFile = path.join(app.getPath('userData'), 'main.log');
this.setupLogging();
}
setupLogging() {
// 重定向控制台输出到文件
const logStream = fs.createWriteStream(this.logFile, { flags: 'a' });
const originalLog = console.log;
const originalError = console.error;
console.log = (...args) => {
const message = `[INFO] ${new Date().toISOString()}: ${args.join(' ')}\n`;
logStream.write(message);
originalLog.apply(console, args);
};
console.error = (...args) => {
const message = `[ERROR] ${new Date().toISOString()}: ${args.join(' ')}\n`;
logStream.write(message);
originalError.apply(console, args);
};
}
}
// 初始化日志系统
new MainProcessLogger();
渲染进程日志
// preload.js
const { ipcRenderer } = require('electron');
// 重写渲染进程的console方法
const originalConsole = { ...console };
['log', 'error', 'warn', 'info', 'debug'].forEach(method => {
console[method] = (...args) => {
// 发送到主进程记录
ipcRenderer.send('renderer-log', {
level: method,
message: args.join(' '),
timestamp: new Date().toISOString()
});
// 保持原有功能
originalConsole[method].apply(console, args);
};
});
日志级别与分类
class LogLevel {
static DEBUG = 0;
static INFO = 1;
static WARN = 2;
static ERROR = 3;
}
class Logger {
constructor(level = LogLevel.INFO) {
this.level = level;
}
debug(...args) {
if (this.level <= LogLevel.DEBUG) {
this._log('DEBUG', args);
}
}
info(...args) {
if (this.level <= LogLevel.INFO) {
this._log('INFO', args);
}
}
warn(...args) {
if (this.level <= LogLevel.WARN) {
this._log('WARN', args);
}
}
error(...args) {
if (this.level <= LogLevel.ERROR) {
this._log('ERROR', args);
}
}
_log(level, args) {
const message = `[${level}] ${new Date().toISOString()}: ${args.join(' ')}`;
console.log(message);
}
}
日志轮转与归档
const fs = require('fs');
const path = require('path');
const zlib = require('zlib');
class LogRotator {
constructor(logDir, maxSize = 10485760, maxFiles = 10) {
this.logDir = logDir;
this.maxSize = maxSize;
this.maxFiles = maxFiles;
}
rotateIfNeeded(logFile) {
try {
const stats = fs.statSync(logFile);
if (stats.size > this.maxSize) {
this.rotate(logFile);
}
} catch (error) {
// 文件不存在,不需要轮转
}
}
rotate(logFile) {
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const rotatedFile = path.join(
this.logDir,
`${path.basename(logFile, '.log')}-${timestamp}.log.gz`
);
// 压缩并归档旧日志
const input = fs.createReadStream(logFile);
const output = fs.createWriteStream(rotatedFile);
const gzip = zlib.createGzip();
input.pipe(gzip).pipe(output).on('finish', () => {
// 清空原日志文件
fs.writeFileSync(logFile, '');
this.cleanupOldFiles();
});
}
cleanupOldFiles() {
const files = fs.readdirSync(this.logDir)
.filter(file => file.endsWith('.gz'))
.map(file => ({
name: file,
time: fs.statSync(path.join(this.logDir, file)).mtime.getTime()
}))
.sort((a, b) => b.time - a.time);
// 删除超出数量限制的旧文件
if (files.length > this.maxFiles) {
files.slice(this.maxFiles).forEach(file => {
fs.unlinkSync(path.join(this.logDir, file.name));
});
}
}
}
性能监控日志
class PerformanceMonitor {
constructor() {
this.metrics = new Map();
this.startTimes = new Map();
}
start(metricName) {
this.startTimes.set(metricName, process.hrtime());
}
end(metricName) {
const startTime = this.startTimes.get(metricName);
if (!startTime) return null;
const diff = process.hrtime(startTime);
const duration = diff[0] * 1000 + diff[1] / 1000000; // 毫秒
this.recordMetric(metricName, duration);
return duration;
}
recordMetric(metricName, value) {
if (!this.metrics.has(metricName)) {
this.metrics.set(metricName, []);
}
this.metrics.get(metricName).push(value);
console.log(`[PERF] ${metricName}: ${value.toFixed(2)}ms`);
}
getSummary() {
const summary = {};
for (const [metricName, values] of this.metrics) {
const sorted = values.sort((a, b) => a - b);
summary[metricName] = {
count: values.length,
min: Math.min(...values),
max: Math.max(...values),
avg: values.reduce((a, b) => a + b, 0) / values.length,
p95: sorted[Math.floor(sorted.length * 0.95)]
};
}
return summary;
}
}
错误追踪与报告
class ErrorTracker {
constructor() {
this.setupErrorHandlers();
}
setupErrorHandlers() {
// 主进程错误处理
process.on('uncaughtException', (error) => {
this.logError('uncaughtException', error);
});
process.on('unhandledRejection', (reason, promise) => {
this.logError('unhandledRejection', reason);
});
// 渲染进程错误处理(通过preload脚本)
}
logError(type, error) {
const errorInfo = {
timestamp: new Date().toISOString(),
type,
message: error.message,
stack: error.stack,
code: error.code
};
console.error(`[ERROR] ${JSON.stringify(errorInfo)}`);
// 可以在这里添加错误上报逻辑
this.reportError(errorInfo);
}
reportError(errorInfo) {
// 发送到错误监控服务
// fetch('https://error-reporting-service.com/api/errors', {
// method: 'POST',
// headers: { 'Content-Type': 'application/json' },
// body: JSON.stringify(errorInfo)
// });
}
}
日志分析工具集成
const { exec } = require('child_process');
class LogAnalyzer {
static analyzeLogs(logFile, options = {}) {
return new Promise((resolve, reject) => {
const commands = [];
if (options.search) {
commands.push(`grep -n "${options.search}" "${logFile}"`);
}
if (options.errorOnly) {
commands.push(`grep -n "\\[ERROR\\]" "${logFile}"`);
}
if (options.stats) {
commands.push(`awk '
/\\[ERROR\\]/ { errors++ }
/\\[WARN\\]/ { warnings++ }
/\\[INFO\\]/ { infos++ }
END {
print "Errors:", errors
print "Warnings:", warnings
print "Infos:", infos
print "Total:", errors + warnings + infos
}
' "${logFile}"`);
}
if (commands.length === 0) {
commands.push(`cat "${logFile}"`);
}
exec(commands.join(' | '), (error, stdout, stderr) => {
if (error) {
reject(error);
} else {
resolve(stdout);
}
});
});
}
}
最佳实践总结
日志策略规划
配置建议表格
| 环境 | 日志级别 | 输出目标 | 轮转策略 |
|---|---|---|---|
| 开发 | DEBUG | 控制台+文件 | 按大小(10MB) |
| 测试 | INFO | 文件 | 按大小(50MB) |
| 生产 | WARN | 文件+监控服务 | 按天+按大小 |
性能考量
// 避免在生产环境记录过多DEBUG日志
if (process.env.NODE_ENV !== 'production') {
logger.debug('详细调试信息');
}
// 使用参数化日志避免不必要的字符串拼接
logger.info('用户操作: %s, 参数: %o', action, params);
// 异步日志记录避免阻塞主线程
setImmediate(() => {
logger.info('异步日志消息');
});
通过实施这些日志管理策略,你可以构建一个健壮的Electron应用监控体系,及时发现和解决运行中的问题,提升应用质量和用户体验。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



