问题:项目中用log4js可以正常写入日志到文件中,后来使用pm2启动后,发现文件不能写入到文件了。网上查了很久,并没有找到原因,所以就想到了查看源码的方法,来查找到底是哪里出了问题。

先看相关的配置

log4js配置:

1
2
3
4
5
6
7
{
"type": "dateFile",
"filename": "logfile/",
"pattern": "flow-yyyy-MM-dd-hh.log",
"alwaysIncludePattern": true,
"layout": { "type": "messagePassThrough" }
}

pm2配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{
"apps": [
{
"name": "project_name",
"script": "index.js",
"exec_mode": "cluster",
"instances": 4,
"instance_var": "INSTANCE_ID",
"max_memory_restart": "1G",
"autorestart": true,
"node_args": [],
"watch": ["config", "data", "lib","public", "index.js"],
"watch_options": {
"usePolling": true
},
"args": [],
"env": {},
"env_dev": {
...
},
"env_production": {
...
}
}
]
}

log4js无法自动生成对应时间格式的文件

正常直接启动会在配置log目录下生成一个flow-yyyy-MM-dd-hh.log格式的文件,但是用pm2启动的时候并没有生成。

log4js的调用方法为require('log4js').getLogger(),所以就直接在源码中找getLogger
1、通过package.json中的main我们确定它的主入口文件为./lib/log4js
2、找到getLogger方法

1
2
3
4
5
6
7
8
9
10
11
/**
* Get a logger instance.
* @static
* @param loggerCategoryName
* @return {Logger} instance of logger for the category
*/
function getLogger(category) {
const cat = category || 'default';
debug(`creating logger as ${isMaster() ? 'master' : 'worker'}`);
return new Logger((isMaster() ? sendLogEventToAppender : workerDispatch), cat);
}

其中有个isMaster()方法,判断是否为主进程

1
2
3
4
5
6
7
function isPM2Master() {
return config.pm2 && process.env[config.pm2InstanceVar] === '0';
}
function isMaster() {
return config.disableClustering || cluster.isMaster || isPM2Master();
}

可以看到isPM2Master是通过config中的pm2参数和pm2InstanceVar来确定的。
所以我们需要在log4js的配置中增加这两个配置。

1
2
3
4
{
pm2: true,
pm2InstanceVar: "INSTANCE_ID" // 与pm2的配置对应
}

pm2 中NODE_APP_INSTANCE 特定的环境变量可以用来判断主从进程

此时重启pm2后(需要pm2 delete不然pm2配置文件不生效),已经可以看到生成的文件了,但是日志还是不能写入。
接着看源码:

workerDispatch方法

1
2
3
4
function workerDispatch(logEvent) {
debug(`sending message to master from worker ${process.pid}`);
process.send({ topic: 'log4js:message', data: serialise(logEvent) });
}

此方法为工作进程接收到log事件,并发出消息,必定有接收的地方,通过查找代码发现此方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function configure(configurationFileOrObject) {
// ...
if (config.disableClustering) {
debug('Not listening for cluster messages, because clustering disabled.');
} else {
// PM2 cluster support
// PM2 runs everything as workers - install pm2-intercom for this to work.
// we only want one of the app instances to write logs
if (isPM2Master()) {
debug('listening for PM2 broadcast messages');
process.removeListener('message', receiver);
process.on('message', receiver);
} else if (cluster.isMaster) {
debug('listening for cluster messages');
cluster.removeListener('message', receiver);
cluster.on('message', receiver);
} else {
debug('not listening for messages, because we are not a master process');
}
}

我们可以看到在if条件中有监听的操作,但是,后面的监听函数没有执行,后来才注意到里面的三行注释,需要安装pm2-intercom;在执行了pm2 install pm2-intercom后,果然可以了,日志成功写入到了文件中。

—-publish by CEditor