关于node.js:winston PM2两次记录uncaughtException

winston + PM2 logging uncaughtException twice

我将Winston用于我的NodeJS应用程序,以具有结构化的日志记录格式(JSON),稍后我将对其进行处理并通过Filebeat发送到Logstash。

如PM2和12factor.net所建议,我正在使用winston.transports.Console传输进行日志记录,并让PM2处理我的ecosystem.json中的stdoutstderr

在我的logger.js模块中,我具有以下内容:

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
"use strict";
const winston = require("winston");
// Remove logging on console
winston.remove(winston.transports.Console);
// env
const env = process.env.NODE_ENV || 'development';
const isDev = env ==="development";
// The default Console transport
const defaultLogLevel = isDev ?"debug" :"info";

const consoleTransport = new winston.transports.Console({
    level: defaultLogLevel,
    colorize: true,
    json: true,
    timestamp: true,
    exceptionsLevel:"error",
    prettyPrint: false,
    handleExceptions: true,
    humanReadableUnhandledException: false,
    exitOnError: true
});

const defaultOptions = {
    transports: [
    consoleTransport
    ]
};


module.exports = function(options)
{
    let initOpts = defaultOptions;
    if(options)
    {
        Object.assign(initOpts, options);
    }
    const logger = new winston.Logger(initOpts);
    // suppress any logger errors
    logger.emitErrs = false;
    winston.handleExceptions(consoleTransport);

    return logger;
};

在我的主应用程序文件server.js中,我运行以下代码来模拟引发未处理的异常,需要并没有找到虚假的依赖项:

1
2
3
4
5
"use strict";
var log         = require('./logger')({name:"api-messages"});
log.info("some info message");
log.error("some error message");
var notFound = require("someInexistentPackage");

最后,在我的PM2 ecosystem.json中,我具有以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
 "apps":
  [
    {
     "name"            :"my-app",
     "script"          :"server.js",
     "max_restarts"    : 1,
     "error_file"      :"~/.pm2/logs/my-app.log",
     "out_file"        :"~/.pm2/logs/my-app.log",
     "merge_logs"      : true

    }
  ]
}

当我运行pm2 start ecosystem.json时,日志文件的内容如下:

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
{
 "level":"info",
 "message":"some info message",
 "timestamp":"2017-06-08T16:10:05.967Z"
}
{
 "level":"error",
 "message":"some error message",
 "timestamp":"2017-06-08T16:10:05.968Z"
}
Error: Cannot find module 'someInexistentPackage'
    at Function.Module._resolveFilename (module.js:470:15)
    at Function.Module._load (module.js:418:25)
    at Module.require (module.js:498:17)
    at require (internal/module.js:20:19)
    at Object. (/Users/mps/Sites/my-app/server.js:10:20)
    at Module._compile (module.js:571:32)
    at Object.Module._extensions..js (module.js:580:10)
    at Module.load (module.js:488:32)
    at tryModuleLoad (module.js:447:12)
    at Function.Module._load (module.js:439:3)
{
 "date":"Thu Jun 08 2017 13:10:05 GMT-0300 (-03)",
 "process": {
   "pid": 50862,
   "uid": 501,
   "gid": 20,
   "cwd":"/Users/mps/Sites/my-app",
   "execPath":"/Users/mps/.nvm/versions/node/v7.10.0/bin/node",
   "version":"v7.10.0",
   "argv": [
     "/Users/mps/.nvm/versions/node/v7.10.0/bin/node",
     "/Users/mps/.nvm/versions/node/v7.10.0/lib/node_modules/pm2/lib/ProcessContainerFork.js"
    ],
   "memoryUsage": {
     "rss": 36724736,
     "heapTotal": 9879552,
     "heapUsed": 6309680,
     "external": 63983
    }
  },
 "os": {
   "loadavg": [
      2.49560546875,
      2.228515625,
      2.0205078125
    ],
   "uptime": 329559
  },
 "trace": [
    {
     "column": 15,
     "file":"module.js",
     "function":"Module._resolveFilename",
     "line": 470,
     "method":"_resolveFilename",
     "native": false
    },
    {
     "column": 25,
     "file":"module.js",
     "function":"Module._load",
     "line": 418,
     "method":"_load",
     "native": false
    },
    {
     "column": 17,
     "file":"module.js",
     "function":"Module.require",
     "line": 498,
     "method":"require",
     "native": false
    },
    {
     "column": 19,
     "file":"internal/module.js",
     "function":"require",
     "line": 20,
     "method": null,
     "native": false
    },
    {
     "column": 20,
     "file":"/Users/mps/Sites/my-app/server.js",
     "function": null,
     "line": 10,
     "method": null,
     "native": false
    },
    {
     "column": 32,
     "file":"module.js",
     "function":"Module._compile",
     "line": 571,
     "method":"_compile",
     "native": false
    },
    {
     "column": 10,
     "file":"module.js",
     "function":"Module._extensions..js",
     "line": 580,
     "method":".js",
     "native": false
    },
    {
     "column": 32,
     "file":"module.js",
     "function":"Module.load",
     "line": 488,
     "method":"load",
     "native": false
    },
    {
     "column": 12,
     "file":"module.js",
     "function":"tryModuleLoad",
     "line": 447,
     "method": null,
     "native": false
    },
    {
     "column": 3,
     "file":"module.js",
     "function":"Module._load",
     "line": 439,
     "method":"_load",
     "native": false
    }
  ],
 "stack": [
   "Error: Cannot find module 'someInexistentPackage'",
   "    at Function.Module._resolveFilename (module.js:470:15)",
   "    at Function.Module._load (module.js:418:25)",
   "    at Module.require (module.js:498:17)",
   "    at require (internal/module.js:20:19)",
   "    at Object. (/Users/mps/Sites/my-app/server.js:10:20)",
   "    at Module._compile (module.js:571:32)",
   "    at Object.Module._extensions..js (module.js:580:10)",
   "    at Module.load (module.js:488:32)",
   "    at tryModuleLoad (module.js:447:12)",
   "    at Function.Module._load (module.js:439:3)"
  ],
 "level":"error",
 "message":"uncaughtException: Cannot find module 'someInexistentPackage'",
 "timestamp":"2017-06-08T16:10:05.972Z"
}

如您所见,PM2正在将未捕获的异常添加到日志文件,即此代码:

1
2
3
4
5
6
7
8
9
10
11
Error: Cannot find module 'someInexistentPackage'
    at Function.Module._resolveFilename (module.js:470:15)
    at Function.Module._load (module.js:418:25)
    at Module.require (module.js:498:17)
    at require (internal/module.js:20:19)
    at Object. (/Users/mps/Sites/my-app/server.js:10:20)
    at Module._compile (module.js:571:32)
    at Object.Module._extensions..js (module.js:580:10)
    at Module.load (module.js:488:32)
    at tryModuleLoad (module.js:447:12)
    at Function.Module._load (module.js:439:3)

这破坏了日志的格式/结构,因为它是纯文本,而不是我从winston接收到的格式化JSON。

我确信这是PM2问题/配置错误,因为如果运行node server.js,则控制台输出只是JSON对象。

我是否必须在PM2 ecosystem.json文件中设置特定属性?

如何告诉PM2忽略Node的uncaughtException

我想出的唯一选择(我还没有尝试过)是将我的winston传输更改为winston.transports.File,将所有日志重定向到stdout,并告诉PM2将stderr保存在不同的文件,但是我宁愿坚持使用控制台方法。


防止PM2登录uncaughtException的一种方法是将--no-pmx作为标志传递,即pm2 start --no-pmx myapp.js或将pmx: false添加到ecosystem.json文件。

这将禁用PMX注入PM2,由PM2记录此uncaughtException