Como consolar.regista um erro com o traço da pilha no nó.js?

tenho tentado depurar a minha aplicação de nós para encontrar a fonte de um erro no meu registo que aparece apenas como "Error: Can't set headers after they are sent", sem qualquer informação de traço ou qualquer contexto.

Por acaso, acho que arranjei isto... Eu estou usando connect-timeout e eu estava continuando a processar um callback passado para uma operação de rede assíncrona, que o callback eventualmente tentaria fazer um res.send(), apesar de req.timedout ter sido definido como 'verdadeiro' por connect-timeout durante a operação de rede.

Mas ainda assim ... não percebo porque é que o meu registo não mostrava informações para este erro. Em qualquer lugar que um erro seja devolvido no meu código, eu o logro para a consola com:

console.log(err);

Se existe informação de rastreio disponível no objecto err, e isto parece estar colocado em err.stack, a declaração acima não deveria descarregar o conteúdo inteiro de err (incluindo err.stack) para o registo da consola? Meu entendimento é que eu não estaria perdendo qualquer informação fazendo o acima, comparado por exemplo. to:

console.log(err.stack);

mas posts Como Este parecem sugerir o contrário (embora o post ligado já tenha sido atualizado).

na verdade vou mais longe, e adiciono algum texto relevante para ajudar a localizar o erro:

console.log('error in dodgyFunction:', err);
Mas apesar disso, eu ainda estava recebendo apenas "[[[4]]", SEM NENHUM do contexto que eu colocaria. Isso seria porque esta mensagem de erro da consola é saída dentro de uma biblioteca externa (como express)? Eu pensei que bibliotecas externas deveriam enviar erros de volta para o código principal a tratar em conformidade?

Editar: aqui está um exemplo de onde coloco a minha verificação de erro e tempo-limite, no topo da função de callback passada para a operação async:

var execFile = require('child_process').execFile;
execFile('dodgycommand', options, function(error, stdout, stderr) {
    if (req.timedout) {
        console.log('timeout detected whilst running dodgycommand, so aborting...');
        return;
    }
    if (error) {
        console.log('error running dodgycommand:', error);
        res.sendStatus(400);
        return;
    }

    // ... it's safe to continue ...

}
Basicamente, sigo este mesmo padrão.

Author: RJFalconer, 2017-03-01

3 answers

Acabei de descobrir o que se estava a passar, e espero que isto ajude outros a evitar este erro de principiante.

Para alguns dos meus erros de Registo, eu estava a usar algo como o seguinte, usando a concatenação de texto para construir a mensagem de erro:

console.log('error in function abc: ' + err + ' whilst doing xyz');

Enquanto em outros lugares eu estava usando algo como o seguinte, apenas passando as peças da mensagem de erro como argumentos separados para console.log:

console.log('error in function xyz:', err, 'whilst doing abc');
Agora vejo que estes resultados são diferentes!

A o primeiro deve ser estringificado err para que possa ser concatenado com as outras partes da mensagem, e de acordo com isto, ao fazê-lo usa apenas a parte da mensagem.

No entanto, na última forma, o objecto err deve ser processado por console.log não adulterado e despejado no seu conjunto.

Isto explica porque às vezes não via todo o conteúdo do erro, como esperava, e outras vezes via.

Quanto às mensagens de log da consola colocadas por outras bibliotecas, outra coisa a verificar é que não está a filtrar as partes 'stack' das mensagens de registo no seu visualizador de registos... acontece que eu era (a fim de economizar na quota de log... am using papertrail)... d'Oh. Eu estava fazendo isso filtrando quaisquer linhas começando com ____at (quatro espaços seguidos por 'at'), por exemplo ____at Request.self.callback.

 4
Author: drmrbrewer, 2017-03-03 08:22:58

Instalei n agora e posso confirmar o seguinte:

Nó 4.0.0

Se usar console.log(err) imprime apenas a mensagem de erro.

Nó 7.7.0 (último)

Usando console.log(err) imprime a mensagem de erro e a pilha completa.


Confirmei que este comportamento mudou na versão 6.0.0. Então, se você usar uma versão mais antiga, eu sugiro que você atualize seu Nodo.js ou use console.log(err.stack) em vez disso, para imprimir a pilha completa.

 3
Author: Zanon, 2017-03-01 17:25:57
O teu padrão é geralmente comum, embora eu diga que, regra geral, não gosto, mais sobre isso num segundo. Quanto à sua pergunta principal, é muito difícil responder com base no que forneceu. Se você mostrar o código real ao invés do" eu geralmente sigo este padrão", ele pode ajudar. Mas é igualmente possível que o erro estivesse sendo jogado em algum lugar que você não estava esperando, e então seu console.log não estava sendo chamado de todo. Parece que estás ... à procura de melhores práticas, então vou dar-lhe o que eu acho que é o melhor que eu encontrei até agora.

Primeiro, não uses console.log para registar. Não é horrível, mas podes fazer muito, muito melhor. O meu favorito é usar morgan como middleware para registar as informações do pedido, e depurar para registar as aplicações.

Com debug pode configurar níveis de Registo personalizados e ouvir o nível que quiser com o nível de granularidade que quiser. É tudo controlado por configurar a variável de ambiente de depuração, e na produção você pode redirecionar para o arquivo ou qualquer outro destino que você quiser. Além disso, muitos módulos de nós (incluindo Express e Connect) usam a depuração como seu logger sob a capa, de modo que ao ajustar a sua variável de depuração você pode ver tanto ou pouco de seu registro interno como você deseja. Muito útil para descobrir o que correu mal onde.

Segundo, como eu disse, Eu não uso o padrão que você tem em tudo quando se trata de roteamento. Eu ... descobri que é fácil enviar cabeçalhos acidentalmente mais de uma vez se eu não tiver cuidado, então meu middleware sempre retorna {[[5]} e as respostas são enviadas apenas em manipuladores reais que eu posso ter certeza de disparar apenas uma vez. Quando se trata de erro, eu sempre Passo next(e) que eu posso então lidar em uma função de manipulador de erros. Eu também criei a bibliotecapraeter para fornecer erros padrão com base em códigos de Estado web e um manipulador de erros Genérico.

O padrão parece algo como isto:

// middleware function to put something on the request object
app.use((req, res, next) => {
  MyModel.doSomething((e, thing) => {
    if (e) return next(e);
    if (!thing) return next(new NotFound()); // NotFound is an error in praeter that equates to a 404. 
    req.thing = thing;
    return next();
  });
});

Depois

// log in here is a reference to my debug configured log object
app.use((err, req, res, next) => {
  log.error(err);
  log.error(err.stack);
  return res.status(err.statusCode || 500).send(err.message)
});

Note que este é um exemplo simples de um manipulador de erros final. Eu muitas vezes tenho vários desses onde eu poderia lidar com diferentes códigos de erro de forma diferente, dependendo das necessidades da aplicação.

 2
Author: Paul, 2017-03-01 13:19:05