0. 背景
Electron 的结构中包含两种类型的进程:主进程和渲染器进程。通过源码运行项目时,在主进程中使用 console.log()
打印的信息会被输出到运行项目的终端窗口中,而渲染器进程的内容则会显示在网页开发者工具的控制台里。希望能统一到一个地方来显示日志。
而且,有时候用 console.log()
会用得比较随意。所以还是想有一个专门的日志工具,使用它提供的功能。
1. 安装
可以使用 yarn
安装:
yarn add log4js
2. 引入
无论是在主进程还是渲染器进程使用 log4js,都希望日志能显示到同一个地方。这可以通过进程间通信(IPC)来实现,需要用到预加载(preload)脚本。
首先,可以先为 log4js 创建一个专门的文件 src/utils/log.js
,设计相关的接口:
const log4js = require('log4js');
const getLogger = (category, level = 'all') => {
const logger = log4js.getLogger(category);
logger.level = level;
return logger;
};
export const trace = (event, category, message) => {
getLogger(category).trace(message);
};
export const debug = (event, category, message) => {
getLogger(category).debug(message);
};
export const info = (event, category, message) => {
getLogger(category).info(message);
};
export const warn = (event, category, message) => {
getLogger(category).warn(message);
};
export const error = (event, category, message) => {
getLogger(category).error(message);
};
export const fatal = (event, category, message) => {
getLogger(category).fatal(message);
};
export const mark = (event, category, message) => {
getLogger(category).mark(message);
};
然后,在 src/main.js
文件中引入这些接口:
+import {debug, error, fatal, info, mark, trace, warn} from "./utils/log";
// ...
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
-app.on('ready', createWindow);
+app.on('ready', () => {
+ ipcMain.on('log4js:trace', trace);
+ ipcMain.on('log4js:debug', debug);
+ ipcMain.on('log4js:info', info);
+ ipcMain.on('log4js:warn', warn);
+ ipcMain.on('log4js:error', error);
+ ipcMain.on('log4js:fatal', fatal);
+ ipcMain.on('log4js:mark', mark);
+
+ createWindow();
+});
之后便可通过预加载脚本 src/preload.js
将接口暴露给渲染器进程:
// See the Electron documentation for details on how to use preload scripts:
// https://www.electronjs.org/docs/latest/tutorial/process-model#preload-scripts
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('getLogger', {
trace: (category, message) => ipcRenderer.send('log4js:trace', category, message),
debug: (category, message) => ipcRenderer.send('log4js:debug', category, message),
info: (category, message) => ipcRenderer.send('log4js:info', category, message),
warn: (category, message) => ipcRenderer.send('log4js:warn', category, message),
error: (category, message) => ipcRenderer.send('log4js:error', category, message),
fatal: (category, message) => ipcRenderer.send('log4js:fatal', category, message),
mark: (category, message) => ipcRenderer.send('log4js:mark', category, message),
});
这样在主进程和渲染器进程中就都可以调用同样的日志接口了。
3. 使用
3.1 主进程
例如有一个被主进程使用的 src/utils/syscall.js
文件,其中有个 ping()
方法,调用时想要记录一下日志:
+import {info} from "./log";
+
const util = require('util');
const exec = util.promisify(require('child_process').exec);
export const ping = (event, target) => {
const ipv4Pattern = /((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})(\.((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})){3}/; // IPv4
const domainNamePattern = /[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+\.?/; // 域名
if ((!ipv4Pattern.test(target)) && (!domainNamePattern.test(target))) {
return Promise.reject(new Error(
target.toString() + ' is neither a valid IPv4 address nor a valid domain name.'
));
}
+ info(null, 'syscall.js', `ping ${target}`);
return exec('ping ' + target);
};
控制台中会打印出:
[2024-03-26T11:55:46.850] [INFO] syscall.js - ping 127.0.0.1
3.2 渲染器进程
假设有个 src/components/SampleComponent.vue
文件要记录日志:
await window.syscall.ping(form.ip).then(() => { // ping 目标地址
+ window.getLogger.info('SampleComponent.vue', `ping ${form.ip} success`);
// ...
}).catch((error) => {
+ window.getLogger.error('SampleComponent.vue', error.message);
// ...
});
控制台会显示:
[2024-03-26T11:55:49.957] [INFO] SampleComponent.vue - ping 127.0.0.1 success
[2024-03-26T11:57:22.315] [ERROR] SampleComponent.vue - Error invoking remote method 'ping': Error: Command failed: ping 0.0.0.0
4. 配置
要修改 log4js 的配置,可以修改 src/utils/log.js
文件中 getLogger()
方法的内容。例如在将日志输出到控制台同时保存到文件。
-const getLogger = (category, level = 'all') => {
- const logger = log4js.getLogger(category);
- logger.level = level;
- return logger;
+const getLogger = (category, level = 'info') => {
+ log4js.configure({
+ appenders: {
+ console: { type: 'console' },
+ dateFile: {
+ type: 'dateFile',
+ filename: 'logs/log.log'
+ }
+ },
+ categories: {
+ default: { appenders: ['console', 'dateFile'], level: level }
+ }
+ });
+ return log4js.getLogger(category);
};
网友评论