对于任何的客户端应用,开发者都但愿可以在用户上的手上记录下相关信息以便了解真实的使用状况。javascript
通常状况下,分为如下两种信息:java
正常日志json
在不涉及隐私的状况下,让开发者了解用户使用客户端的详细状况,从这些状况中提炼的信息可以让开发者根据用户的使用状况更好地优化产品windows
崩溃日志api
用户的使用环境千差万别,有时候可能会让客户端崩溃。崩溃日志的收集,有利于让开发人员更好地定位,解决问题xcode
对于日志的处理,通常分为 收集 、上报、分析等多个步骤,本文主要细分讲述 Electron 客户端应用的日志收集步骤bash
正常日志,能够理解成可以被开发者应用代码正常捕获的行为日志服务器
按照 Electron 应用的结构,日志收集的结构状况以下图:app
解释:框架
main process 为主进程,render processes 为各个窗口的渲染进程
logger 为 main process 下的日志收集组件,能够把收集到的日志信息保存到本地 和 上报到远程服务器
Electron.remote 是 Electron 框架自带的对象,能够做为主进程与渲染进程的桥接,让渲染进程可以访问到主进程的 logger 组件,底层原理是基于 ipc 信息的封装
render processes 经过 Electron.remote 访问到 logger,而后就能够把本身的日志信息经过 logger 进行收集
渲染进程中的 worker 和 service worker 比较特别,它们是独立于渲染进程,没法直接从 Electron.remote 中访问 logger,可是它们能够经过 ipc message 把日志信息传递到特定的渲染进程,而后再经过渲染进程经过 logger 记录日志信息
主进程下的子进程 child processes,没法直接访问 Electron.remote,经过 ipc message,能够把收集的日志信息传递给主进程,主进程再经过 logger 记录日志信息
针对应用的需求,设置好 logger 组件
/** * @param {string} level * @param {string} text */
function log(level, text) {
var args = Array.prototype.slice.call(arguments, 1);
args = args.map(function(arg) {
return arg instanceof Error ? arg.stack + EOL : arg;
});
text = util.format.apply(util, args);
var msg = {
level: level,
text: text,
date: new Date()
};
var transports = module.exports.transports;
for (var i in transports) {
// jshint -W089
if (!transports.hasOwnProperty(i) || typeof transports[i] !== 'function') {
continue;
}
if (!compareLevels(transports[i].level, level)) {
continue;
}
try{
transports[i].call(module.exports, msg);
}catch(err){
console.error('Logger Error: ', err);
}
}
}
复制代码
把 logger 对象暴露到全局
global.log = require('./src/base/log');
复制代码
主进程记录日志
// GPU进程崩溃
app.on('gpu-process-crashed', function(){
log.error('GPU进程崩溃,程序退出');
app.exit(0);
});
// 当全部窗口被关闭了,退出
app.on('window-all-closed', function() {
// 在 OS X 上,一般用户在明确地按下 Cmd + Q 以前
// 应用会保持活动状态
if (process.platform != 'darwin') {
app.quit();
}
});
复制代码
渲染进程记录日志
经过 remote 获取 logger 对象
window.logger = remote.getGlobal('log');
复制代码
在渲染进程上,把 logger 的方法与 console 方法进行绑定
function extendConsole(){
const logFn = console.log;
console.log = function(...args){
logFn(args);
if(logger && logger.log){
logger.info(args);
}
}
// ...
}
复制代码
service worker 经过 ipc message 通信记录日志
向接管了Service Worker的渲染进程传输日志信息
//service worker
function sendMsg(type, ...args){
self.clients.matchAll().then(
clientList => {
clientList.forEach(client => {
client.postMessage({
event: type,
data: args
});
})
}
)
}
//目标渲染进程
navigator.serviceWorker.addEventListener('message', function(swe){
// log swe.data
});
复制代码
崩溃日志,能够理解为不受开发人员应用所能控制的崩溃
开发人员的应用没法直接捕获这种崩溃错误,须要借助框架和操做系统底层的收集机制进行日志的收集
Electron 已经提供了崩溃日志的收集机制,详情能够查看 Electron 的 官方文档:崩溃日志
大体流程以下图:
解释:
crash reporter 为 Electron 提供的收集机制,它能够收集应用程序没法捕获的崩溃错误,收集后,能够把信息保存到本地或者上传到指定服务器
主进程 与 渲染进程 的崩溃错误均可以被正确收集
注意点:任何进程,想要可以被收集崩溃信息,都必须在进程上显式调用 crashReporter.start 方法,以初始化收集处理
主进程能够直接访问 crashReporter,渲染进程能够经过 Electron.remote 访问 crashReporter,子进程能够经过 process.crashReporter 访问崩溃收集对象
每一个进程都尽量添加上崩溃收集;
崩溃收集的信息统一上报到服务器上,方便开发人员收集统计;
以下图(每份崩溃报告都有一个对应的 json文件 与 对应的 minidump文件):
获取到对应的崩溃文件后,须要对 dump 文件进行分析,推荐工具为 google 的 breakpad。关于 breakpad,不一样平台有不一样的安装方法,须要各位看官经过官方文档仔细安装(mac 要用 xcode 进行编译,windows须要额外安装 gcc)
假设已经安装好了 breakpad,那么咱们会有两个工具:dump_syms 和 minidump_stackwalk,在这里,咱们关键会用到 minidump_stackwalk这个工具
经过以下命令行,把 minidump 文件解析并存储结果到 output.txt 中
minidump_stackwalk 15dcad6faa9e9914ae9016d794c391a8 > ./output.txt
复制代码
结果以下,经过输出的详细信息,开发人员就能更好解决问题~