Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

This question is related to Express under Node.js.

Background

With Express, you stack up several "middleware" modules to handle a specific HTTP request.

app.use(express.logger());
app.use(express.favicon()));
app.use(express.cookieParser());
app.use(express.session());
app.use(app.router);

When a request comes in, Express goes through all of the functions in the stack until a module handles the request. Specifically, each middleware module is a function (req, res, next), and that function will call next() to call the next module in the stack. Functions can modify data in the req or res objects if they choose.

The Problem

I was interested in making custom log middleware modules for my Express application, and I needed to use more than one module at a time. The data I need to log cannot be logged until the response has been sent to the client. (I am tracking bytes sent, and the amount of time a connection was established.) Unfortunately, Node.js's HTTP response object does not raise an end event. Presumably, this is because it is up to the application to do any "end" work prior to calling .end() on the response object in the first place. Express does not expose a hook prior to .end() being called.

Express/Connect's built-in logger middleware is able to track how long a request was running, so I decided to see what method they use. They actually replace the res.end function with their own. When their own res.end function is called, it swaps the old res.end back in place, finishes logging, then calls res.end with the parameters that their own res.end was called with. Their method works, but can be problematic when a handful of modules try to do this. (The order in which res.end function replacement occurs becomes critical.)

My Solution

I have used their method to patch res to fire end events so that any module in the chain can subscribe to that event in the usual way.

function (req, res, next) {
    var end = res.end;

    res.end = function () {
        res.end = end;
        res.emit('end');
        res.end.apply(this, arguments);
    }

    next();
}

Then, all my logger module has to do is:

res.on('end', function () { /* log here */ });

Is there any problem with what I have done? Is there a better way to do it?

share|improve this question

1 Answer 1

I did the following for something similar. This uses the closure to create a chain-of-command pattern.

function (req, res, next) {
  var realEnd = res.end;
  res.end = function () {
    var realEndArgs = arguments;
    req.log.info ("HTTP RESPONSE CODE " + res.statusCode);
    realEnd.apply (res, realEndArguments);
  }
  next();
}

You could instead of logging the status code, do res.emit('end') as you described, or anything else, before returning control to the original res.end (which may itself be a replacement). Or, you could do directly in this function whatever you want to do in response to the end event.

Note: I explicitly store res.end's arguments in realEndArguments in case I later add anything asynchronous, because arguments would then refer to the asynchronous action's callback's arguments instead of res.end's arguments. A subtle bug I ran into at first.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.