Adicionando timestamps a todas as mensagens do console

94

Tenho um projeto completo, implantado e baseado em Express, com muitas instruções console.log () e console.error (). O projeto é executado usando o forever, direcionando stdout e stderr para 2 arquivos separados.

Tudo funciona muito bem, mas agora não tenho carimbos de data / hora - para saber exatamente quando os erros ocorreram.

Posso fazer algum tipo de pesquisa / substituição em todo o meu código ou usar algum módulo npm que substitui o console em cada arquivo, mas não quero mexer em todos os arquivos de modelo / rota, a menos que seja absolutamente necessário.

Existe uma maneira, talvez um middleware Express, que me permitiria adicionar um carimbo de data / hora a cada chamada feita, ou tenho que adicioná-lo manualmente?

Travelling Tech Guy
fonte

Respostas:

119

Acontece que você pode substituir as funções do console na parte superior do arquivo app.js e fazer com que tenha efeito em todos os outros módulos. Obtive resultados mistos porque um dos meus módulos é bifurcado como um child_process. Depois de copiar a linha para o início desse arquivo também, tudo funcionará.

Para registro, instalei o módulo console-stamp ( npm install console-stamp --save) e adicionei esta linha no topo de app.js e childProcess.js:

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

Meu problema agora era que o :dateformato do logger de conexão usa o formato UTC, em vez do que estou usando nas outras chamadas de console. Isso foi facilmente corrigido registrando meu próprio formato de hora (e como efeito colateral, exigindo o dateformatmódulo que console stampvem com, em vez de instalar outro):

// 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'));

Agora meus arquivos de registro parecem organizados (e melhor ainda, analisáveis):

[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
...
Travelling Tech Guy
fonte
2
Não consegui encontrar os documentos para ele, mas parece que ": mm" se refere ao mês e ": MM" é o formato que você realmente deseja usar
Laurent Sigal
2
você deve alterar a parte dos minutos de acordo com o que diz @ user603124. Para minutos, a string é : MM ( github.com/starak/node-console-stamp )
sucotronic
Obrigado pelo comentário. Corrigido!
Travelling Tech Guy
2
Parece que você não precisa mais colocar HH: MM: ss.l entre colchetes - está fazendo isso automaticamente
Jeff
3
FYI loggerfoi substituído por morgan github.com/senchalabs/connect#middleware
vtellier
36

módulo: "log-timestamp" funciona para mim.

consulte https://www.npmjs.com/package/log-timestamp

npm install log-timestamp

Simples de usar

console.log('Before log-timestamp');
require('log-timestamp');
console.log('After log-timestamp');

Resultado

Before log-timestamp
[2012-08-23T20:08:32.000Z] After log-timestamp
Sunding Wei
fonte
26

Crie um arquivo com o seguinte:

var log = console.log;

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

Exija-o em seu aplicativo antes de registrar qualquer coisa. Faça o mesmo console.errorse necessário.

Observe que esta solução irá destruir a variável insertion ( console.log("he%s", "y") // "hey") se você estiver usando isso. Se você precisar disso, apenas registre o carimbo de data / hora primeiro:

log.call(console, Date.now());
log.apply(console, arguments);
Andreas Hultgren
fonte
2
Não se for o mesmo aplicativo / processo. O console é um objeto global, portanto, se você sequestrar uma de suas funções dessa forma, ele continuará sendo sequestrado para todos os arquivos que compartilham esse objeto global.
Andreas Hultgren
Então, isso deve / poderia ser colocado no arquivo app.js?
Travelling Tech Guy de
1
Sim. <min 15 chars ...>
Andreas Hultgren
1
Eu recomendo usar o carimbo do console em vez
Jacek Pietal
1
Esta não é uma boa solução - ela destrói a inserção de variável (portanto, não pode ser usada como substituição) ou imprime a data e a saída do log em linhas diferentes.
George Y.
16

Se você deseja uma solução sem outra dependência externa, mas deseja manter todas as funcionalidades do console.log (vários parâmetros, inserção de variável), pode usar o seguinte código:

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

Você pode modificar a função formatConsoleDate para formatar a data como quiser.

Este código precisa ser escrito apenas uma vez no topo do seu arquivo JavaScript principal.

console.log("he%s", "y") imprimirá algo assim:

[12:22:55.053] hey
leszek.hanusz
fonte
4
Obrigado, essa resposta "sem dependências" era exatamente o que eu precisava.
RdR
9

Você também pode usar o pacote log-timestamp . É bastante simples e personalizável também.

Chetan
fonte
3
app.use(morgan('[:date[web]] :method :url :status :res[content-length] - :remote-addr - :response-time ms'))
thxmxx
fonte
2

Esta não é uma resposta direta, mas você já olhou para winston.js? Possui uma tonelada de opções de registro, incluindo registro em um arquivo json ou banco de dados. Esses sempre têm carimbos de data / hora por padrão. Apenas um pensamento.

Zeke Alexandre Nierenberg
fonte
Eu examinei muitas coisas, agora, gostaria de adicionar algo a um projeto existente implantado - sem mexer muito no código
Travelling Tech Guy
2

Esta implementação é simples, suporta a funcionalidade original de console.log (passando um único objeto e substituição de variável), não usa módulos externos e imprime tudo em uma única chamada para 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.
fonte
2

Se desejar, você pode criar um logger personalizado para seu aplicativo, estendendo a construção do Node na classe "Console". Por favor, consulte a seguinte implementação

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

Depois disso, você pode usá-lo em seu código como:

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

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

Shivam Shekhar
fonte
1

Estou tentando substituir o consoleobjeto - parece estar funcionando bem. Para usar, salve o código abaixo em um arquivo e importe-o para sobrescrever o objeto proxy e use-o normalmente.

(Observe que isso requer transpilação de babel e não funcionará em ambientes que não suportam o Proxyconstrutor JavaScript , como o 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],
  }
)

Basicamente, sobrescrevo o objeto de console por um objeto proxy JavaScript. Quando você liga .log,.warn etc., o console sobrescrito verificará se o que você está chamando é uma função; nesse caso, ele injetará uma data na instrução de log como o primeiro parâmetro, seguido por todos os seus parâmetros.

Acho que o consoleobjeto realmente faz muito e não o entendo totalmente. Então eu só interceptar console.log, console.info, console.warn, console.errorchama.

Zach Smith
fonte
-1

Use ouvinte de eventos assim,

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

feliz codificação :)

JsWizard
fonte