Skip to content

Instantly share code, notes, and snippets.

@pguillory
Created December 5, 2010 23:51
Show Gist options
  • Save pguillory/729616 to your computer and use it in GitHub Desktop.
Save pguillory/729616 to your computer and use it in GitHub Desktop.
Hooking into Node.js stdout
var util = require('util')
function hook_stdout(callback) {
var old_write = process.stdout.write
process.stdout.write = (function(write) {
return function(string, encoding, fd) {
write.apply(process.stdout, arguments)
callback(string, encoding, fd)
}
})(process.stdout.write)
return function() {
process.stdout.write = old_write
}
}
console.log('a')
console.log('b')
var unhook = hook_stdout(function(string, encoding, fd) {
util.debug('stdout: ' + util.inspect(string))
})
console.log('c')
console.log('d')
unhook()
console.log('e')
console.log('f')
@isaacs
Copy link

isaacs commented Dec 6, 2010

JS FTW!

@tommedema
Copy link

Thanks, I've created a use case example for when one uses cluster and wishes to separate per-process output.

module.exports = function(mediator) {

    var fs          = require('fs'),
        path        = require('path'),
        config      = require('../../config'),
        env         = process.env,
        stdout      = process.stdout,
        stderr      = process.stderr,
        workerId    = env.NODE_WORKER_ID || env.NODE_UNIQUE_ID,
        logDir      = config.debug.logDir,
        stdoutFile  = path.resolve(logDir, 'worker#' + workerId + '.log'),
        stderrFile  = path.resolve(logDir, 'worker#' + workerId + '.err.log');

    //run prior to boot ready
    mediator.once('boot.init', function() {

        //create a new stdout file stream
        var stdoutFS = fs.createWriteStream(stdoutFile, {
            encoding: 'utf8',
            flags   : 'a+'
        });

        //create a new stderr file stream
        var stderrFS = fs.createWriteStream(stderrFile, {
            encoding: 'utf8',
            flags   : 'a+'
        });

        //pipe stdout to a worker file
        var unhookStdout = hook_writestream(stdout, function(string, encoding, fd) {
            stdoutFS.write(string, encoding || 'utf8');
        });
        console.log('\n\nPrepared new stdout hook to worker file.');

        //pipe stderr to a worker file
        var unhookStderr = hook_writestream(stderr, function(string, encoding, fd) {
            stderrFS.write(string, encoding || 'utf8');
        });
        console.log('\n\nPrepared new stderr hook to worker file.');

        //unhook when things go wrong
        stdoutFS.once('close', function() {
            unhookStdout();
            console.log('Unhooked stdout.');
        });
        stdoutFS.once('error', function(err) {
            unhookStdout();
            console.error('Error: Unhooked stdout due to error %j.', err);
        });
        stderrFS.once('close', function() {
            unhookStderr();
            console.log('Unhooked stderr.');
        });
        stderrFS.once('error', function(err) {
            unhookStderr();
            console.error('Error: Unhooked stderr due to error %j.', err);
        });

    });

    function hook_writestream(stream, callback) {
        var old_write = stream.write;

        stream.write = (function(write) {
            return function(string, encoding, fd) {
                write.apply(stream, arguments);
                callback(string, encoding, fd);
            };
        })(stream.write);

        return function() {
            stream.write = old_write;
        };
    }

};

@tleen
Copy link

tleen commented Jan 30, 2013

Thanks for this :)

@mikermcneil
Copy link

Thanks!

@mikermcneil
Copy link

Crystallized it a bit further here:
https://github.com/mikermcneil/fixture-stdout

@stringparser
Copy link

@IonicaBizau
Copy link

Awesome magic here. 😄 Thanks!

@dmarcelino
Copy link

Thanks @pguillory, I've used it on debug-logger

@wthit56
Copy link

wthit56 commented Oct 12, 2015

Genius! In my implementation, I created a new object to add my changes to:

var stdout = process.stdout;
var shadow = process.stdout = Object.create(process.stdout);
shadow.original = stdout;
shadow.write = function() {
    // stdout.original.write.apply(stdout, arguments); // not shown
    result.stdout += data;
};

(Which didn't work. Ignore me. ;P)

@jkuri
Copy link

jkuri commented Jan 28, 2016

Thanks for this.

@karlpokus
Copy link

FYI. Commenting out this line mutes the log too.

@reliasn
Copy link

reliasn commented Dec 14, 2016

This implementation redirects both stdout and stderr to a log file:

var log_file = require('fs').createWriteStream(__dirname + '/log.txt', {flags : 'w'})

function hook_stream(stream, callback) {
  var old_write = stream.write

  stream.write = (function(write) {
    return function(string, encoding, fd) {
      write.apply(stream, arguments)  // comments this line if you don't want output in the console
      callback(string, encoding, fd)
    }
  })(stream.write)

  return function() {
    stream.write = old_write
  }
}

console.log('a')
console.error('b')

var unhook_stdout = hook_stream(process.stdout, function(string, encoding, fd) {
  log_file.write(string, encoding)
})

var unhook_stderr = hook_stream(process.stderr, function(string, encoding, fd) {
  log_file.write(string, encoding)
})

console.log('c')
console.error('d')

unhook_stdout()
unhook_stderr()

console.log('e')
console.error('f')

It should print in the console

a
b
c
d
e
f

and in the log file:

c
d

@dherault
Copy link

dherault commented Jun 10, 2019

This one works with AWS Lambda:

function hookStdout(callback) {
  const boundProcessStdout = process.stdout.write.bind(process.stdout)
  const boundProcessStderr = process.stderr.write.bind(process.stderr)

  process.stdout.write = (string, encoding, fd) => {
    boundProcessStdout(string, encoding, fd)
    callback(string, encoding, fd, false)
  }

  process.stderr.write = (string, encoding, fd) => {
    boundProcessStderr(string, encoding, fd)
    callback(string, encoding, fd, true)
  }

  return () => {
    process.stdout.write = boundProcessStdout
    process.stderr.write = boundProcessStderr
  }
}

@MaiTrinh
Copy link

MaiTrinh commented Nov 3, 2020

thanks for this.

@nexensys
Copy link

Thank you so much for this! I'm trying to make an advanced CLI for a nextjs app, but webpack uses stdout and messes up all of the formatting!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment