winston + PM2 两次记录 uncaughtException

winston + PM2 logging uncaughtException twice

我正在为我的 NodeJS 应用程序使用 winston,以获得结构化日志记录格式 (JSON),稍后我将处理并通过 Filebeat 发送到 Logstash。

按照 PM2 和 12factor.net 的建议,我正在使用 winston.transports.Console 传输记录并让 PM2 处理我 ecosystem.json 中的 stdoutstderr .

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

"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 我 运行 此代码模拟抛出未处理的异常,需要假依赖但未找到:

"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 我有以下内容:

{
  "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日志文件的内容如下:

{
  "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"
}

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

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)

这破坏了我日志的 format/structure,因为它是纯文本而不是我从 winston.

收到的格式化 JSON

我确信这是 PM2 issue/misconfiguration,因为如果我 运行 node server.js 控制台输出只是 JSON 个对象。

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

如何让 PM2 忽略 Node 的 uncaughtException

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

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

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