126

I have a complete, deployed, Express-based project, with many console.log() and console.error() statements throughout. The project runs using forever, directing the stdout and stderr to 2 separate files.

It all works quite well, but now I'm missing timestamps - to know exactly when errors occurred.

I can do some kind of search/replace throughout my code, or use some npm module that overrides console in each file, but I do not want to touch every model/route file, unless I absolutely have to.

Is there a way, perhaps an Express middleware, that would allow me to add a timestamp to every call made, or do I have to manually add it?

Traveling Tech Guy
  • 27,194
  • 23
  • 111
  • 159
  • Closely related: [Is it possible to bind a date/time to a console log?](http://stackoverflow.com/questions/18410119/is-it-possible-to-bind-a-date-time-to-a-console-log) and [console.log timestamps in Chrome?](http://stackoverflow.com/questions/12008120/console-log-timestamps-in-chrome) – Dan Dascalescu Jul 18 '15 at 04:00
  • @Vipul this is a NodeJS question (backend), not a Chrome console question. – Traveling Tech Guy May 22 '21 at 05:42

15 Answers15

143

It turns out, you can override the console functions at the top of the app.js file, and have it take effect in every other module. I got mixed results because one of my modules is forked as a child_process. Once I copied the line to the top of that file as well, all works.

For the record, I installed the module console-stamp (npm install console-stamp --save), and added this line to the top of app.js and childProcess.js:

// add timestamps in front of log messages
require('console-stamp')(console, '[HH:MM:ss.l]');

My problem now was that the :date format of the connect logger uses UTC format, rather than the one I'm using in the other console calls. That was easily fixed by registering my own time format (and as a side effect, requiring the dateformat module that console stamp comes with, rather than installing another one):

// since logger only returns a UTC version of date, I'm defining my own date format - using an internal module from console-stamp
express.logger.format('mydate', function() {
    var df = require('console-stamp/node_modules/dateformat');
    return df(new Date(), 'HH:MM:ss.l');
});
app.use(express.logger('[:mydate] :method :url :status :res[content-length] - :remote-addr - :response-time ms'));

Now my log files look organized (and better yet, parseable):

[15:09:47.746] staging server listening on port 3000
[15:09:49.322] connected to database server xxxxx successfully
[15:09:52.743] GET /product 200 - - 127.0.0.1 - 214 ms
[15:09:52.929] GET /stylesheets/bootstrap-cerulean.min.css 304 - - 127.0.0.1 - 8 ms
[15:09:52.935] GET /javascripts/vendor/require.js 304 - - 127.0.0.1 - 3 ms
[15:09:53.085] GET /javascripts/product.js 304 - - 127.0.0.1 - 2 ms
...
Nico Schlömer
  • 53,797
  • 27
  • 201
  • 249
Traveling Tech Guy
  • 27,194
  • 23
  • 111
  • 159
  • 2
    I could not find the docs for it, but it appears that ":mm" will refer to the month, and ":MM" is the format that you actually want to use – Laurent Sigal Feb 28 '14 at 18:25
  • 2
    you shoud change the minutes part according to what says @user603124 . For minutes the string is **:MM** (https://github.com/starak/node-console-stamp) – sucotronic Sep 30 '14 at 08:58
  • 2
    Seems like you no longer need to wrap HH:MM:ss.l in brackets -- it is doing it automatically – Jeff May 13 '15 at 15:53
  • 4
    FYI `logger` has been replaced by `morgan` https://github.com/senchalabs/connect#middleware – VincentTellier May 23 '18 at 10:56
75

The module log-timestamp works for me.

npm install log-timestamp

It's simple to use:

console.log('Before log-timestamp');
require('log-timestamp');
console.log('After log-timestamp');
Before log-timestamp
[2012-08-23T20:08:32.000Z] After log-timestamp
Nico Schlömer
  • 53,797
  • 27
  • 201
  • 249
Sunding Wei
  • 1,803
  • 17
  • 12
34

Create a file with the following:

var log = console.log;

console.log = function(){
  log.apply(console, [Date.now()].concat(arguments));
};

Require it in your app before you log anything. Do the same for console.error if needed.

Note that this solution will destroy variable insertion (console.log("he%s", "y") // "hey") if you're using that. If you need that, just log the timestamp first:

log.call(console, Date.now());
log.apply(console, arguments);
Andreas Hultgren
  • 14,763
  • 4
  • 44
  • 48
23

If you want a solution without another external dependency but you want to keep the full functionalities of console.log (multiple parameters, variable insertion) you can use the following code:

var log = console.log;

console.log = function () {
    var first_parameter = arguments[0];
    var other_parameters = Array.prototype.slice.call(arguments, 1);

    function formatConsoleDate (date) {
        var hour = date.getHours();
        var minutes = date.getMinutes();
        var seconds = date.getSeconds();
        var milliseconds = date.getMilliseconds();

        return '[' +
               ((hour < 10) ? '0' + hour: hour) +
               ':' +
               ((minutes < 10) ? '0' + minutes: minutes) +
               ':' +
               ((seconds < 10) ? '0' + seconds: seconds) +
               '.' +
               ('00' + milliseconds).slice(-3) +
               '] ';
    }

    log.apply(console, [formatConsoleDate(new Date()) + first_parameter].concat(other_parameters));
};

You can modify the formatConsoleDate function to format the date how you want.

This code needs to be written only once on top of your main JavaScript file.

console.log("he%s", "y") will print something like this:

[12:22:55.053] hey
leszek.hanusz
  • 5,152
  • 2
  • 38
  • 56
  • 5
    Thanks, this "no dependencies" answer was exactly what I needed. – RdR Aug 03 '17 at 08:10
  • that messes up the file:line information :( – Deian May 21 '21 at 16:03
  • 2
    That solution can't output objects. It will simply out "[Object object]". Instead of concatenating the timestamp and first_parameter, just treat them as two parameters: `log.apply(console, [formatConsoleDate(new Date()), first_parameter].concat(other_parameters));` (replace '+' with ',') – Alexander Uhl Mar 03 '22 at 12:10
12

You could also use the log-timestamp package. It's quite straightforward, and customizable as well.

Chetan
  • 1,724
  • 2
  • 14
  • 18
7

This implementation is simple, supports original functionality of console.log (passing a single object, and variable substitution), doesn't use external modules and prints everything in a single call to console.log:

var origlog = console.log;

console.log = function( obj, ...placeholders ){
    if ( typeof obj === 'string' )
        placeholders.unshift( Date.now() + " " + obj );
    else
    {
        // This handles console.log( object )
        placeholders.unshift( obj );
        placeholders.unshift( Date.now() + " %j" );
    }

    origlog.apply( this, placeholders );
};
George Y.
  • 11,307
  • 3
  • 24
  • 25
7

If you wish, you may create a custom logger for your application by extending the Node's build in "Console" class. Kindly refer to the following implementation

"use strict";

const moment = require('moment');
const util = require('util');
const Console = require('console').Console;

class Logger extends Console {
    constructor(stdout, stderr, ...otherArgs) {
        super(stdout, stderr, ...otherArgs);
    }

    log(...args) {
        super.log(moment().format('D MMM HH:mm:ss'), '-', util.format(...args));
    }

    error(...args) {
        super.error(moment().format('D MMM HH:mm:ss'), '-', util.format(...args));
    }
}

module.exports = (function() {
    return new Logger(process.stdout, process.stderr); 
}());

After that, you may use it in your code as :

const logger = require('./logger');

logger.log('hello world', 123456);
logger.error('some error occurred', err);
5

A more rudimentary approach, to avoid installing external modules, could be implementing a simple function such as:

function timeStamp(message){
    console.log ( '[' + new Date().toISOString().substring(11,23) + '] -', message )
}

and then I simply call it in this way:

timeStamp('this is my logline!!');

the result will be:

 LOG  [15:22:30.682] - this is my logline!!

of course you can format the date in the best format you need and expand the function to console.error, debug, etc.

Manuel Sansone
  • 339
  • 4
  • 5
3

This isn't a direct answer, but have you looked into winston.js? It has a ton more logging options including logging to a json file or database. These always have timestamps by default. Just a thought.

Zeke Nierenberg
  • 2,216
  • 1
  • 19
  • 30
  • I've looked into many things, right now, I'd like to add something to an existing, deployed project - without touching too much of the code – Traveling Tech Guy Sep 15 '13 at 15:55
3
app.use(morgan('[:date[web]] :method :url :status :res[content-length] - :remote-addr - :response-time ms'))
slm
  • 15,396
  • 12
  • 109
  • 124
thxmxx
  • 447
  • 4
  • 9
3

An answer suitable for 2022

const log = console.log;

console.log = function(){
    log.apply(console, [new Date(), ...arguments]);
};
Rotem
  • 2,306
  • 3
  • 26
  • 44
1

You can use a function util.log from https://nodejs.org/api/util.html.

Be aware that it was deprecated since version 6.0.0.

For higher versions you should "Use a third party module instead."

Goran Stoyanov
  • 2,311
  • 1
  • 21
  • 31
mlosev
  • 5,130
  • 1
  • 19
  • 31
1

I'm trying overwriting the console object - seems to be working well. To use, save the code below in a file, and then import to overwrite the proxy object, and then use as normal.

(Note this requires babel transpilation and won't work in environments that don't support the JavaScript Proxy constructor such as IE 11).

import console from './console-shadow.js'

console.log(...)
console.warn(...)
console.error(...)
// console-shadow.js

// Only these functions are shadowed by default
const overwrites = ['log', 'warn', 'info', 'error']

export default new Proxy(
  // Proxy (overwrite console methods here)
  {},

  // Handler
  {
    get: (obj, prop) =>
      prop in obj
        ? obj[prop]
        : overwrites.includes(prop)
        ? (...args) => console[prop].call(console, new Date(), ...args)
        : console[prop],
  }
)

Basically I overwrite the console object with a JavaScript proxy object. When you call .log, .warn, etc. the overwritten console will check if what you are calling is a function, if so it will inject a date into the log statement as the first parameter, followed by all your parameters.

I think the console object actually does a lot, and I don't fully understand it. So I only intercept console.log, console.info, console.warn, console.error calls.

Zach Smith
  • 8,458
  • 13
  • 59
  • 133
0

You can allways use log4j, as its written in doku to use third party tools.

I can recommend using the log4j. It does gives more than time but this is a good entry for the future impovements as well.

Please find below example implementation

var log4js = require('log4js');
var logger = log4js.getLogger();
logger.level = 'info';
logger.log('your log message');

Example output

[2023-05-01T15:24:12.948] [INFO] default - your log message

mati kepa
  • 2,543
  • 19
  • 24
-1

Use event listener like this,

process.on('error', function() { 
   console.log('Error Occurred.');

   var d = Date(Date.now()).toString();
   console.log.call(console, d); // Wed Aug 07 2019 23:40:07 GMT+0100 (GMT+01:00)
});

happy coding :)

JsWizard
  • 1,663
  • 3
  • 21
  • 48