问题
I am using winston for my NodeJS apps, to have a structured logging format (JSON) that later I will process and send to Logstash via Filebeat.
As suggested by PM2 and 12factor.net I'm logging using winston.transports.Console
transport and letting PM2 to handle the stdout
and stderr
in my ecosystem.json
.
In my logger.js
module I have the following:
"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;
};
In my main app file server.js
I run this code to simulate an unhandled Exception being thrown, a fake dependency is being required and not found:
"use strict";
var log = require('./logger')({name: "api-messages"});
log.info("some info message");
log.error("some error message");
var notFound = require("someInexistentPackage");
Finally, in my PM2 ecosystem.json
I have the following:
{
"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
}
]
}
When I run pm2 start ecosystem.json
the contents of the log files are the following:
{
"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.<anonymous> (/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.<anonymous> (/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"
}
As you can see, PM2 is adding the uncaught Exception to the log file, i.e. this code:
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.<anonymous> (/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)
This breaks the format/structure of my logs, since it's plain text instead of the formatted JSON I receive from winston
.
I'm confident this is a PM2 issue/misconfiguration, because if I run node server.js
the console output are just the JSON objects.
Do I have to setup a specific property in PM2 ecosystem.json
file?
How can I tell PM2 to ignore Node's uncaughtException
?
The only alternative I have come up with (which I haven't tried yet), is to change my winston transport to winston.transports.File
, redirect ALL logs to stdout
and tell PM2 to save stderr
in a different file, but I'd rather stick with the console aproach.
回答1:
One way to prevent PM2 from logging the uncaughtException
is to pass --no-pmx
as a flag, i.e. pm2 start --no-pmx myapp.js
or to add pmx: false
to the ecosystem.json
file.
This disables PMX injection into PM2, which is the responsible for logging this uncaughtException
.
来源:https://stackoverflow.com/questions/44441017/winston-pm2-logging-uncaughtexception-twice