Devo capturar exceções apenas para registrá-los?
-
01-07-2019 - |
Pergunta
Devo captura exceções para fins de registro?
public foo(..) { try { ... } catch (Exception ex) { Logger.Error(ex); throw; } }
Se eu tiver isso no lugar em cada uma das minhas camadas (DataAccess, negócios e WebService) significa que a exceção é registrado várias vezes.
Será que faz sentido fazê-lo se as minhas camadas estão em projetos separados e apenas as interfaces públicas têm try / catch neles? Por quê? Por que não? Existe uma abordagem diferente, eu poderia usar?
Solução
Definitivamente não. Você deve encontrar o local correto para alça a exceção (na verdade, fazer algo, como catch-e-não-rethrow), e, em seguida, registrá-lo. Você pode e deve incluir todo o rastreamento de pilha, é claro, mas seguindo sua sugestão seria lixo o código com blocos try-catch.
Outras dicas
A menos que você estiver indo para mudar a exceção, você deve registra somente no nível onde você está indo para lidar com o erro e não relançar-lo. Caso contrário, seu log só tem um monte de "ruído", 3 ou mais da mesma mensagem registrada, uma vez em cada camada.
Meu melhor prática é:
- Apenas try / catch em métodos públicos (em geral; obviamente, se você está prendendo para um erro específico que você gostaria de verificar para ele lá)
- Apenas log na camada UI direita antes de suprimir o erro e redirecionar para uma página de erro / formulário.
A regra geral é que você só pegar uma exceção se você pode realmente fazer algo sobre isso. Assim, na camada de negócios ou de dados, você só iria capturar a exceção na situação de como isto:
try
{
this.Persist(trans);
}
catch(Exception ex)
{
trans.Rollback();
throw ex;
}
tentativas camada My Business / dados para economizar os dados -. Se uma exceção é gerada, todas as transações são revertidas ea exceção é enviado para a camada UI
Na camada de interface do usuário, você pode implementar um manipulador de exceção comum:
Application.ThreadException + = novo ThreadExceptionEventHandler (Application_ThreadException);
Qual então lida com todas as exceções. Pode registrar a exceção e, em seguida, exibir uma resposta amigável:
static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
{
LogException(e.Exception);
}
static void LogException(Exception ex)
{
YYYExceptionHandling.HandleException(ex,
YYYExceptionHandling.ExceptionPolicyType.YYY_Policy,
YYYExceptionHandling.ExceptionPriority.Medium,
"An error has occurred, please contact Administrator");
}
No código UI real, você pode pegar indivíduo exceção de se você está indo fazer algo diferente -. Tais como exibição de uma mensagem amigável diferente ou modificar a tela, etc
Além disso, apenas como um lembrete, sempre tentar manipular erros - por exemplo, dividir por 0 -., Em vez de lançar uma exceção
É uma boa prática é traduzir as exceções . Não basta registrá-los. Se você quer saber o motivo específico uma exceção foi lançada, lançar exceções específicas:
public void connect() throws ConnectionException {
try {
File conf = new File("blabla");
...
} catch (FileNotFoundException ex) {
LOGGER.error("log message", ex);
throw new ConnectionException("The configuration file was not found", ex);
}
}
Use suas próprias exceções para embrulhar exceção inbuild. Desta forma, você pode distinta entre erros conhecidos e desconhecidos quando pegar exceção. Isso é útil se você tiver um método que chama outros métodos que são provavelmente jogando excpetions reagir em cima esperado e fracassos inesperados
Você pode querer pesquisar estilos de tratamento de exceções padrão, mas o meu entendimento é o seguinte:. Exceções alça no nível onde você pode adicionar detalhes extra para a exceção, ou no nível em que você vai apresentar a exceção para o usuário
no seu exemplo você está fazendo nada, mas captura a exceção, registrando-a e jogando-o novamente .. porque não basta pegá-lo ao mais alto nível com um try / catch em vez de dentro de cada método se tudo que você está fazendo é logging -lo?
i só iria lidar com isso nesse nível, se você estava indo para adicionar algumas informações úteis para a exceção antes de jogá-lo novamente - enrole a exceção em uma nova exceção de criar que tem informações úteis para além do texto exceção baixo nível que geralmente significa pouco a ninguém sem algum contexto ..
Às vezes você precisa de dados que não está disponível onde a exceção é tratada log. Nesse caso, é apropriado para iniciar sessão só para tirar isso da informação.
Por exemplo (pseudocódigo Java):
public void methodWithDynamicallyGeneratedSQL() throws SQLException {
String sql = ...; // Generate some SQL
try {
... // Try running the query
}
catch (SQLException ex) {
// Don't bother to log the stack trace, that will
// be printed when the exception is handled for real
logger.error(ex.toString()+"For SQL: '"+sql+"'");
throw ex; // Handle the exception long after the SQL is gone
}
}
Este é semelhante ao corte retroativo (minha terminologia), onde tampão um log de eventos, mas não escrevê-los a menos que haja um evento de disparo, como uma exceção sendo lançada.
Se você é obrigado a registrar todas as exceções, então é uma idéia fantástica. Dito isto, registrando todas as exceções sem outra razão não é tal uma boa idéia.
Você pode querer fazer logon no mais alto nível, que normalmente é sua interface do usuário ou código de serviço web. Registrando várias vezes é uma espécie de resíduos. Além disso, você quer saber toda a história quando você está olhando para o log.
Em uma de nossas aplicações, todas as nossas páginas são derivados de um objeto BasePage, e esse objeto alças lidar com a exceção e registro de erros.
Se essa é a única coisa que ele faz, eu acho que é melhor para remover o try / catch de partir dessas classes e deixe a exceção ser elevado para a classe que é responsável em lidar com eles. Dessa forma, você obter apenas um log por exceção dando-lhe registros mais claras e até mesmo você pode registrar o stacktrace para que você não vai perder a partir de onde a exceção foi originada.
Meu método é registrar as exceções apenas no manipulador. O manipulador de 'real' por assim dizer. Caso contrário, o log será muito difícil de ler e o código menos estruturado.
Depende da Exceção: se isso realmente não deveria acontecer, eu definitivamente iria registrá-lo. Na outra maneira: se você espera que essa exceção que você deve pensar sobre o design do aplicativo
.De qualquer maneira:. Você deve pelo menos tentar especificar a exceção de que quer relançar, captura ou log
public foo(..)
{
try
{
...
}
catch (NullReferenceException ex) {
DoSmth(e);
}
catch (ArgumentExcetion ex) {
DoSmth(e);
}
catch (Exception ex) {
DoSmth(e);
}
}
Você vai querer fazer logon em um limite de camada. Por exemplo, se a sua camada de negócios pode ser implantado em uma máquina fisicamente separada em um aplicativo de n camadas, então faz sentido para registrar e lançar o erro desta forma.
Desta forma você tem um registro de exceções no servidor e não precisa ir bisbilhotando máquinas clientes para descobrir o que aconteceu.
Eu uso esse padrão em níveis empresariais de aplicativos que usam serviços web Remoting ou ASMX. Com WCF você pode interceptar e registrar uma exceção usando um IErrorHandler anexado ao seu ChannelDispatcher (outro assunto completamente.) - para que você não precisa do / catch / padrão tentativa lance
Você precisa desenvolver uma estratégia para lidar com exceções. Eu não recomendo a captura e relançar. Além do log supérfluo entradas torna o código mais difícil de ler. Considere escrever para o log no construtor para a exceção. Este reservas do try / catch para exceções que deseja recuperar de; tornando o código mais fácil de ler. Para lidar com exceções inesperadas ou irrecuperáveis, você pode querer um try / catch perto da camada mais externa do programa para registrar as informações de diagnóstico.
BTW, se este for C ++ seu bloco catch é a criação de uma cópia do objeto de exceção que pode ser uma fonte potencial de problemas adicionais. Tente pegar uma referência ao tipo de exceção:
catch (const Exception& ex) { ... }
Este Rádio software Engenharia podcast é uma boa referência para as melhores práticas de manipulação de erro. Há realmente 2 palestras.