No momento, estou refatorando um grande subsistema com uma arquitetura de várias camadas e estou lutando para criar uma estratégia eficaz de registro de erros \ manipulação.
Digamos que minha arquitetura consiste nas três camadas a seguir:
- Interface pública (IE um controlador MVC)
- Camada de domínio
- Camada de acesso a dados
Minha fonte de confusão é onde eu devo implementar o log de erros \ manipulação:
A solução mais fácil seria implementar o log no nível superior (ou seja, Public Interface \ MVC Controller). No entanto, isso parece errado, porque significa modificar a exceção através das diferentes camadas e depois registrá-la; em vez de registrar a exceção na origem.
Registrar a exceção na fonte é obviamente a melhor solução, porque eu tenho mais informações. Meu problema é que não consigo capturar todas as exceções na origem sem capturar TODAS as exceções, e na camada de domínio / interface pública, isso levará à captura de exceções que já foram capturadas, registradas e reproduzidas pela camada abaixo .
Outra estratégia possível é uma mistura de # 1 e # 2; onde pego exceções específicas na camada com maior probabilidade de serem lançadas (captura do IE, registro e re-lançamento
SqlExceptions
na camada de acesso a dados) e, em seguida, registro quaisquer outras exceções não capturadas no nível superior. No entanto, isso também exigiria que eu capturasse e registrasse novamente todas as exceções no nível superior, porque não consigo distinguir entre os erros que já foram registrados \ manipulados e os que não o fizeram.
Agora, obviamente, esse é um problema na maioria dos aplicativos de software; portanto, deve haver uma solução padrão para esse problema que resulte em exceções capturadas na origem e registradas uma vez; no entanto, eu simplesmente não consigo ver como fazer isso sozinho.
Observe que o título desta pergunta é muito semelhante a ' Exceções de log em um aplicativo de várias camadas " ', no entanto, as respostas nessa postagem estão sem detalhes e não são suficientes para responder à minha pergunta.
fonte
The easiest solution would be to implement the logging at the top level
- faça isso. Registrar exceções na origem não é uma boa ideia e todos os aplicativos que encontrei que fazem isso eram um PITA para depuração. Deve ser responsabilidade do chamador lidar com exceções.try{ ... } catch(Exception ex) { Log(ex); }
resultaria na mesma exceção sendo registrada em cada camada. (Ele também parece ser bastante ruim prática para ser captura cada exceção em todas as camadas na base de código.)Respostas:
Para suas perguntas:
Ter a bolha de exceção até o nível superior é uma abordagem absolutamente correta e plausível. Nenhum dos métodos da camada superior tenta continuar com algum processo após a falha, que normalmente não pode ser bem-sucedido. E uma exceção bem equipada contém todas as informações necessárias para o log. E não fazer nada sobre exceções ajuda a manter seu código limpo e focado na tarefa principal, e não nas falhas.
Isso é meio correto. Sim, as informações mais úteis estão disponíveis lá. Mas eu recomendo colocar tudo isso no objeto de exceção (se ainda não estiver lá) em vez de registrá-lo imediatamente. Se você fizer logon em um nível baixo, ainda precisará abrir uma exceção para informar aos chamadores que você não concluiu seu trabalho. Isso acaba em vários logs do mesmo evento.
Exceções
Minha principal orientação é capturar e registrar exceções apenas no nível superior. E todas as camadas abaixo devem garantir que todas as informações de falha necessárias sejam transportadas até o nível superior. Dentro de um aplicativo de processo único, por exemplo, em Java, isso significa principalmente não tentar / capturar ou registrar tudo fora do nível superior.
Às vezes, você deseja incluir algumas informações de contexto no log de exceções que não estão disponíveis na exceção original, por exemplo, a instrução SQL e os parâmetros que foram executados quando a exceção foi lançada. Em seguida, você pode capturar a exceção original e lançar novamente uma nova, contendo a original mais o contexto.
Obviamente, a vida real às vezes interfere:
Em Java, às vezes você precisa capturar uma exceção e agrupá-la em um tipo de exceção diferente, apenas para obedecer a algumas assinaturas de método fixo. Mas se você repetir uma exceção, verifique se a repetida contém todas as informações necessárias para o log posterior.
Se você está cruzando uma borda entre processos, geralmente não é possível transferir tecnicamente o objeto de exceção completa, incluindo o rastreamento de pilha. E é claro que a conexão pode se perder. Então, aqui está um ponto em que um serviço deve registrar exceções e, em seguida, tentar o melhor para transmitir o máximo possível de informações de falha através da linha para seu cliente. O serviço deve garantir que o cliente receba um aviso de falha, recebendo uma resposta de falha ou executando um tempo limite em caso de uma conexão interrompida. Isso normalmente resultará na mesma falha no logon duas vezes, uma vez dentro do serviço (com mais detalhes) e uma vez no nível superior do cliente.
Exploração madeireira
Estou adicionando algumas frases sobre o log em geral, não apenas sobre o log de exceções.
Além de situações excepcionais, você deseja que atividades importantes do seu aplicativo também sejam registradas no log. Portanto, use uma estrutura de log.
Tenha cuidado com os níveis de log (ler logs em que informações de depuração e erros sérios não são sinalizados com diferentes de acordo com isso é uma dor!). Os níveis típicos de log são:
Na produção, defina o nível do log como INFO. Os resultados devem ser úteis para um administrador do sistema, para que ele saiba o que está acontecendo. Espere que ele ligue para você para obter assistência ou correção de erros para cada ERRO no log e metade dos AVISOS.
Habilite o nível DEBUG apenas durante sessões de depuração reais.
Agrupe as entradas de log em categorias apropriadas (por exemplo, pelo nome completo da classe do código que gera a entrada), permitindo ativar os logs de depuração para partes específicas do seu programa.
fonte
Estou me preparando para os votos negativos, mas vou me expor e dizer que não tenho certeza se posso concordar com isso.
Ocultar exceções, muito menos registrá-las novamente, é um esforço extra com pouco benefício. Capture a exceção na fonte (sim, mais fácil), registre-a, mas depois não volte a lançar a exceção, apenas relate "erro" ao chamador. "-1", nulo, sequência vazia, alguma enumeração, qualquer que seja. O chamador só precisa saber que a chamada falhou, quase nunca os detalhes horríveis. E esses estarão no seu registro, certo? Nos casos raros em que o chamador precisa dos detalhes, vá em frente e borbulhe, mas não como um padrão automático e impensado.
fonte
null
ou a string vazia? É -1 ou qualquer número negativo? 2. Se o chamador não se importa (ou seja, não verifica), isso leva a erros de acompanhamento não relacionados à causa original, por exemplo, aNullPointerException
. Ou pior: o cálculo continua com valores incorretos. 3. Se o chamador se importaria, mas o programador não acha que esse método falhou, o compilador não o lembra. As exceções não têm esses problemas, você captura ou reproduz novamente.