问题 NodeJS Express - 全球唯一请求ID


是否可以定义每个日志语句中包含的唯一请求ID,而无需将记录器交给每个方法/函数调用?

使用的技术:NodeJS,Express,Winston


10229
2018-04-26 21:03


起源

如果您愿意使用ES6,则可能需要使用 Symbol - MayorMonty
很想,但我现在仍然坚持0.10.13 - smokedice
@MayorMonty会有什么帮助? - milan


答案:


编辑

最后,我创建了一个可以完成所有工作的库。 https://github.com/davicente/express-logger-unique-req-id

它是Winston库的包装器,因此您可以以相同的方式使用它。

如果它对您有所帮助,请告诉我


我们在几个项目中遇到了同样的问题,我无法找到这个问题的任何完整解决方案。我们使用相同的技术(Node.js,Express.js和Winston用于日志) 我找到了一个使用几个库并包装Winston库的解决方案: - node-uuid,用于为每个请求创建唯一标识符 - continuation-local-storage,用于在不同模块之间共享此ID,而不在所有调用中发送req对象。

首先,我需要为每个请求创建和设置唯一标识符。我在中间件中这样做:

var uuid = require('node-uuid');
var createNamespace = require('continuation-local-storage').createNamespace;
var myRequest = createNamespace('my request');

// Run the context for each request. Assign a unique identifier to each request
app.use(function(req, res, next) {
    myRequest.run(function() {
        myRequest.set('reqId', uuid.v1());
        next();
    });
});

之后,我不得不包装Winston库,从上下文中恢复id并添加到日志的消息:

var winston = require('winston');
var getNamespace = require('continuation-local-storage').getNamespace;

// Wrap Winston logger to print reqId in each log
var formatMessage = function(message) {
    var myRequest = getNamespace('my request');
    message = myRequest && myRequest.get('reqId') ? message + " reqId: " + myRequest.get('reqId') : message;
    return message;
};

var logger = {
    log: function(level, message) {
        winstonLogger.log(level, formatMessage(message));
    },
    error: function(message) {
        winstonLogger.error(formatMessage(message));
    },
    warn: function(message) {
        winstonLogger.warn(formatMessage(message));
    },
    verbose: function(message) {
        winstonLogger.verbose(formatMessage(message));
    },
    info: function(message) {
        winstonLogger.info(formatMessage(message));
    },
    debug: function(message) {
        winstonLogger.debug(formatMessage(message));
    },
    silly: function(message) {
        winstonLogger.silly(formatMessage(message));
    }
};
module.exports = logger;

我认为这有点复杂,所以我决定把它写在一篇文章中。您可以从那里获得更多信息: Express.js:使用全局唯一请求ID记录信息 - Node.js 

我希望这有助于解决您的问题。


7
2017-11-13 09:57





这个答案 有一个问题:每次重新启动节点进程时,计数器都会返回0。事实证明,解决这个问题相当简单。您只需添加一个快速中间件,用于标记每个被调用的请求 UUID  使用 UUID 包。

对于uuid Pre-2.0

const uuid = require('uuid');
const app = express();
app.use(function (req, next) {
  req.id = uuid.v4();
  next();
});

对于uuid 3.0+

const uuidv4 = require('uuid/v4');
const app = express();
app.use(function (req, next) {
  req.id = uuidv4();
  next();
});

2
2017-10-10 22:49



这种方法会将问题从将记录器向下移动到每个对象以将请求传递给每个对象。 - smokedice
生成的req.id真的很独特吗?或者,如果重新启动进程,您可以获得具有相同ID的2个会话?正如我在uuid文档中看到的那样,它不使用持久存储。 - Fede E.


在请求处理的最开始,添加类似下面的内容(或将其放在自己的文件中):

var makeID = (function() {
  var index = 0;
  return function() {
   return index++;
  }
})();

app.use(function(req, res, next) {
  req.id = makeID()
  next()
})

这将为每个请求提供唯一(顺序)ID。 不要让人们用它来表明自己,只在内部使用它!

登录Winston时,您可以输入元数据,以便在消息之后显示(在表单中) ${name}=${value}),看起来像这样

app.use(function(req, res) {
  winston.log('info', 'Test Log Message', { id: req.id });
  res.end("Done.")
});

希望这有用。


0
2018-04-28 00:28



这是当前实现的解决方案,但请求Id在其他模块/函数/方法中不可变,而不修改每个相关定义以包括请求Id或记录器。 - smokedice
你不能只使用cookies吗? - MayorMonty
是否需要req才能访问z cookie? - smokedice
是的,或者你可以保存所有打开的连接' req 和 res 在某种全球对象中 - MayorMonty
@smokedice你最终找到了一种方法来记录你的服务中的请求ID而不将requestId或logger传递给其他模块???我现在正在寻找一种方法,似乎无法找到一个好的解决方案。 - Eatdoku