24

Is it possible to define a unique request Id that is included in each log statement without handing the logger to each method/function call?

Technologies in use: NodeJS, Express, Winston

smokedice
  • 900
  • 2
  • 10
  • 25

3 Answers3

19

Edited

Finally, I have created a library that makes all the work. https://github.com/davicente/express-logger-unique-req-id

It is a wrapper of Winston library, so you can use it the same way.

Let me know if it helps you


We had this same problem in several projects, and I couldn't finde any complete solution for this question. We are using same technologies (Node.js, Express.js and Winston for logs) I found a solution to this using a couple of libraries and wrapping Winston library: - node-uuid for creating unique identificators for each request - continuation-local-storage for sharing this IDs among different modules without sending req object in all the calls.

First I need to create and set the unique identificator with each request. I do it in the middleware:

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();
    });
});

After that I had to wrap Winston library, recovering the id from the context and adding to the message of the log:

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;

I think it was a little bit complex, so I decided to write it down in a post. You can get more information from there: Express.js: Logging info with global unique request ID – Node.js

I hope this helps with your problem.

David Vicente
  • 3,091
  • 1
  • 17
  • 27
  • this is for every request, i have read the blog it works fine for the every request it is comming, but if i have a function called test inside how can i set a trace id and use it in the logger.js so whenever the app starts the test function will execute and i can get a trace id – Learner Dec 13 '18 at 05:26
  • I'm sorry, but I don't understand well what you say, can you specify a little bit more? – David Vicente Dec 13 '18 at 16:30
  • I have created a logger using winston , morgan and daily rotate file. The problem is that if i save the code only the log folder and files are generated. if i try to hit the route the folder is not creating basically http req is taken care of morgan but eventhough i combined it it is not working – Learner Jan 18 '19 at 07:43
  • [`node-uuid`](https://www.npmjs.com/package/node-uuid) is deprecated. – ggorlen Jul 26 '21 at 18:22
8

This answer has a problem: the counter goes back to 0 every time the node process is restarted. Turns out there is fairly simple to work around. You simply add an express middleware that tags each request called with a UUID using the uuid package.

For uuid Pre-2.0

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

For uuid 3.0+

const uuidv4 = require('uuid/v4');
const app = express();
app.use(function (req, next) {
  req.id = uuidv4();
  next();
});
milan
  • 11,872
  • 3
  • 42
  • 49
  • 1
    this approach would move the problem from handing the logger down to each object to handing the request down to each object. – smokedice Oct 11 '17 at 18:25
  • Is the generated req.id really unique? Or you can get 2 sessions with the same id if the process is restarted? As I saw in the uuid docs, it doesn't use persistent storage. – Fede E. May 09 '18 at 12:20
  • Deep requires `require('uuid/v4')` are deprecated according to the [uuid readme](https://www.npmjs.com/package/uuid). – ggorlen Jul 26 '21 at 18:26
0

At the very beginning of your request handling add something like the following (or put it in its own file):

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

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

This will give every request a unique (sequential) id. Do not allow people to identify themselves with it, only use it internally!

When logging in Winston, you can enter metadata, to be displayed after the message (in the form ${name}=${value}), which looks like this

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

Hope this is helpful.

bren
  • 4,176
  • 3
  • 28
  • 43
  • That is the solution that is currently implemented, but the request Id is not variable within other modules/functions/methods without modifying every related definition to include the request Id or logger. – smokedice Apr 28 '16 at 10:31
  • Couldn't you just use cookies, then? – bren Apr 28 '16 at 20:22
  • Would one not need req to gain access to z cookies? – smokedice Apr 28 '16 at 20:23
  • Yeah, or you could just save the all the open connections' `req` and `res` in some sort of global object – bren Apr 28 '16 at 20:31
  • 4
    @smokedice did you end up finding a way to log request id through out your service without passing down the requestId or logger to other modules??? im looking for a way to do that right now, can't seem to find a good solution. – Eatdoku May 29 '16 at 07:26
  • @Eatdoku afraid not :( – smokedice May 30 '16 at 21:56
  • btw, index goes back to 0 every time you restart this node process, this doesn't seem like a good long term solution – milan Oct 10 '17 at 21:54
  • Please, @smokedice check my answer, I finally got it after a lot of researchs. I hope my work helps you – David Vicente Nov 17 '17 at 09:17