Por que “log e jogar” é considerado um anti-padrão? [fechadas]

89

Esta questão foi gerada por uma discussão em torno deste artigo , onde não recebi nenhuma boa resposta.

Por que registrar sua exceção e, em seguida, lançá-la novamente (preservando o rastreamento de pilha original, é claro) seria uma má ideia se você não pode lidar com isso de outra forma?

Manu
fonte
Tente pesquisar uma resposta em softwareengineering.stackexchange.com
chharvey

Respostas:

129

Presumo que a resposta seja em grande parte porque você está pegando se não consegue lidar com isso? Por que não deixar quem pode lidar com isso (ou quem fica sem escolha a não ser lidar com isso) registrá-lo, se eles acham que é digno de log?

Se você capturá-lo, registrá-lo e lançá-lo novamente, não haverá como o código upstream saber que você já registrou a exceção e, portanto, a mesma exceção pode ser registrada duas vezes. Ou pior, se todo o código upstream seguir o mesmo padrão, a exceção pode ser registrada um número arbitrário de vezes, uma para cada nível no código que decide capturá-lo, registrá-lo e lançá-lo novamente.

Além disso, alguns podem argumentar que, como lançar e capturar exceções são operações relativamente caras, toda essa captura e relançamento não está ajudando no desempenho do tempo de execução. Nem está ajudando seu código em termos de concisão ou facilidade de manutenção.

aroth
fonte
para mais detalhes, veja os comentários sobre a resposta de Jeff
Manu
8
Um motivo para registrar e relançar uma exclusão seria restringir o motivo e registrar uma mensagem específica.
Mike Argyriou
12
Resposta: pode haver informações úteis de depuração disponíveis no ponto atual da pilha que não estarão disponíveis em outro ponto no rastreamento da pilha
rtconner
Capturar e relançar às vezes é útil se a nova exceção for mais útil com mais informações ou mais específica.
borjab
1
Para gastar com o que @rtconner comentou: em código assíncrono pode ser que o apanhador não tenha o contexto que está disponível para o lançador.
Nir Alfasi
55

Log-and-throw é um bom padrão se a entidade que captura e relança a exceção tem motivos para acreditar que ela contém informações que não serão registradas posteriormente na pilha de chamadas - pelo menos não da maneira mais desejada. Alguns motivos pelos quais isso pode ocorrer:

  1. A exceção pode ser capturada e relançada em um limite da camada de aplicativo e pode conter informações privilegiadas. Seria ruim para uma camada de banco de dados permitir uma exceção dizendo, por exemplo, "Tentativa de adicionar chave duplicada 'fnord' ao campo 'usuários'" para alcançar a camada de aplicativo externa (que por sua vez pode expô-la a um usuário), mas pode ser útil para partes internas do banco de dados para lançar tal exceção e a interface do aplicativo para capturá-la, registrá-la com segurança e relançar uma exceção um pouco menos descritiva.
  2. A exceção pode ser aquela que a camada externa provavelmente espera controlar sem o registro, mas a camada interna pode saber algo que a camada externa não sabe, o que sugere que o registro pode ser útil. Como um exemplo bruto, uma camada de aplicativo do meio pode ser programada para tentar se conectar a um servidor e, se isso não funcionar, tente outro. Inundar o log do aplicativo com mensagens de 'falha de conexão' enquanto um servidor está fora do ar para manutenção pode não ser útil, especialmente porque - da perspectiva do aplicativo, tudo funcionou bem. Pode ser útil encaminhar informações sobre a falha de conexão para um recurso de registro associado a servidores, que poderia filtrar os registros de modo a produzir um relatório de quando o servidor foi ativado e desativado, em oposição a um registro de cada tentativa de conexão .
supergato
fonte
4
# 1 é de fato um caso de uso geral sensato, entretanto o OP perguntou explicitamente sobre "relançar (preservando o rastreamento da pilha original, é claro)", então # 1 não é a resposta certa.
Geoffrey Zheng
@GeoffreyZheng: Isso dependeria de o registro seguro do rastreamento da pilha original contaria como "preservação".
supercat
3
Em relação ao nº 1: expor o conteúdo da exceção (como exibir e.getMessage()/ stacktrace na IU, bem como enviá-lo como resposta REST) ​​deve ser tratado como vulnerabilidade em si, pois a exceção de tempo de execução pode conter qualquer tipo de informação sensível. Em relação ao # 2: você pode relançar a exceção adicionando todas as informações que deseja que o cliente saiba (+ causa raiz), sem necessidade de registrar nada.
Nikita Bosik
@NikitaBosik: Se as exceções devem ou não ser expostas do lado do cliente, os aplicativos que o fazem são suficientemente comuns que o princípio de 'segurança em profundidade' sugere que as mensagens de exceção devem ser eliminadas de informações confidenciais. Além disso, se a camada externa não espera descartar um tipo específico de exceção sem registrá-la nem encaminhar informações para uma entidade externa que possa estar interessada, ter uma camada intermediária para adicionar informações que a camada externa irá ignorar não ajudará qualquer coisa.
supercat
20

Eu acho que o motivo mais simples é que você normalmente teria um único manipulador de nível superior fazendo isso para você, então não há necessidade de poluir o código com esse tratamento de exceção.

O argumento das preocupações transversais é basicamente que é uma perda de tempo lidar com erros que não dizem respeito a você. Muito melhor deixar o erro borbulhar na pilha de chamadas até que um manipulador apropriado seja encontrado.

Em minha opinião, a única vez em que você deve capturar uma exceção é quando pode fazer algo útil com os resultados. Pegar simplesmente para registrar não é útil, porque você poderia centralizar esse trabalho mais adiante.

Jeff Foster
fonte
2
Concordo que você deve ter um único manipulador de nível superior fazendo isso para você. Mas, em minha opinião, esse manipulador de nível superior DEVE ser "registrar e lançar" -ing. Portanto, o argumento gira em torno de EM QUE PONTO "registrar e lançar", não se quer ou não fazer isso?!?
Manu de
@Manu, acho que a questão é que, se for um único manipulador (centralizado), tudo bem. Se você está duplicando código, não está bem. Não acho que seja mais do que isso!
Jeff Foster
5
@Manu - O que o manipulador de nível superior está lançando nesse caso? Parece-me que se houver algo disponível para ele lançar, então não é realmente o manipulador de nível superior. E você definitivamente não quer relançar uma exceção para o próprio ambiente de tempo de execução. Isso fará com que a maioria dos aplicativos pare de funcionar.
aroth
1
@aroth: Entendo, o que você está dizendo é "registrar e manipular" (se você for o manipulador de nível superior) ou "não registrar e lançar (ou manipular de outra forma)" se não for. Obrigado por apontar isso.
Manu
9

O registro e lançamento da IMO é uma violação clara do Princípio da Menor Surpresa.

Se a exceção for tratada adequadamente mais acima na pilha de chamadas, pode não valer a pena uma entrada de log de erros. E então é confuso encontrar uma entrada de log de erro.

Bastian Voigt
fonte
E os logs de não erros?
Su Zhang
6
@SuZhang Não entendi muito bem sua pergunta. Se não houver erro, não há nada para jogar. Claro que você pode e deve escrever logs de não erros.
Bastian Voigt