É mais sensato registrar exceções em uma classe de exceção geral ou de base?

15

Estou no processo de refatoração de um aplicativo Web bastante grande. Um dos principais problemas é o tratamento inconsistente de erros e estou tentando criar uma estratégia sensata. Eu criei um manipulador de erro personalizado, via set_error_handler que essencialmente gera erros de PHP em ErrorExceptions e uma classe de exceção de base personalizada, que herda diretamente de Exception .

Na produção, estou usando um catch-all genérico de exceção, via set_exception_handler , e estou prestes a adicionar o log de exceção * ao mix. Meu dilema é onde fazer o log real, na classe de exceção básica ou no catch-all.

Eu pensei em algumas razões para registrá-lo no catch-all:

  • Existem algumas exceções no código que precisam ser convertidas em algum filho apropriado da classe de exceção base. Até que isso aconteça, nem todas as exceções serão registradas.
  • De alguma forma, parece mais natural fazê-lo de uma maneira geral, uma classe de exceção básica não deve fazer mais do que ser apenas isso. (Pode ser um princípio único de responsabilidade, mas pode ser apenas um sentimento equivocado)

e um motivo para efetuar login na classe de exceção básica:

  • Atualmente, o catch-all é usado apenas na produção. Seria fácil introduzi-lo em nossos outros ambientes (desenvolvimento, teste), mas isso exigiria alguns ajustes, pois os erros são tratados de maneira diferente por ambiente, pois na produção eles são traduzidos para páginas de erro 404/503.

Existe alguma prática aceitável para onde registrar exceções?

* O registro envolverá a gravação em um arquivo de texto primeiro e poderá evoluir para o envio de e-mails para certos tipos de exceções.


Alguns esclarecimentos, solicitados pela resposta do @ unholysampler :

Estou enfrentando uma base de código 2 * 10 ^ 6 sloc, com muitas coisas de terceiros sobre as quais não tenho controle, e parte do código que tenho controle sobre exceções anteriores a datas no PHP. E há também um código recente de baixa qualidade. Estamos nos recuperando de um longo período de intensa pressão em que praticamente tivemos que parar de pensar e apenas hackear.

Estamos refatorando ativamente para solucionar todas as inconsistências e introduzir uma abordagem sensata de tratamento de erros, mas isso levará algum tempo. Estou mais interessado no que fazer até chegar ao ponto em que os erros são tratados adequadamente. Provavelmente vou fazer outra pergunta sobre uma estratégia sensata de exceção em algum momento.

A principal motivação por trás do registro é receber um email no meu telefone sempre que algo de ruim acontece na produção. Eu não me importo se os despejos de dados ficarem enormes, se eles tiverem um trabalho cron excluindo os antigos de vez em quando.

yannis
fonte

Respostas:

11

Em resumo, o único momento em que você deve registrar a existência de uma exceção é quando você está lidando com ela.

Quando você lança uma exceção, é porque seu código atingiu um estado em que não pode prosseguir corretamente. Ao lançar uma exceção, você está representando uma mensagem específica no seu programa sobre o erro que ocorreu. Você não deve capturar uma exceção até estar em um ponto em que ela possa ser tratada adequadamente.

O código que você escreve como parte do aplicativo principal deve estar ciente dos tipos de exceções que podem ser lançadas e quando podem ser lançadas. Se você não pode fazer nada produtivo com uma exceção, não pegue. Não registre uma exceção até que ela esteja sendo manipulada. Somente o código de manipulação sabe o que a exceção significa no contexto do fluxo do programa e como responder a ela. Escrever uma mensagem de log aqui pode fazer sentido aqui. Se você usar uma estrutura de log, poderá definir um nível de log para a mensagem e potencialmente filtrá-la. Isso funciona bem para exceções que podem ocorrer, mas não são críticas e podem ser recuperadas de forma limpa.

Sua exceção é o último esforço para impedir que seu código caia em uma morte feia. Se você chegou até aqui, registra todas as informações de estado e erro que puder. Em seguida, faça o possível para informar ao usuário que o programa está travando antes que tudo pare. Seu objetivo deve ser nunca ter esse código executado.

A incorporação do log na classe base não segue as diretrizes acima. A classe base não sabe nada sobre o estado do código. (O rastreamento da pilha não conta, porque você não escreverá um código que tome decisões com base na análise.) A classe base não pode fazer nada para indicar a gravidade ou como a exceção pode ser tratada. Você não deseja grandes despejos de dados e rastreios de pilha toda vez que houver uma exceção simples que você pode manipular e recuperar de forma limpa.

unholysampler
fonte
Adicionei alguns esclarecimentos sobre a pergunta solicitada por sua resposta. Pelo que entendi, no lado prático da pergunta que você propõe fazer logon no catch-all?
yannis
1
@YannisRizos: Sim, você deve implementar o catch-all como seu primeiro passo. O que eu disse sobre o catch-all foi mais para garantir que você não o usasse como parte normal do seu fluxo de código. A implementação de um manipulador de exceções sem tratamento é importante porque permite obter muitas informações sempre que seu código faz algo ruim.
Unholysampler #
Não há estruturas de registro que possam lidar convenientemente com o conceito de "Aqui estão algumas coisas que devem ser registradas, a menos que sejam substituídas"? Cada camada que vê uma exceção pode substituir os dados da anterior, exceto que, se uma nova exceção for lançada no decorrer da pilha, o último relatório de log não será substituído e, portanto, será gravado. Nenhuma estrutura suporta esse padrão?
Supercat
3

Se o seu idioma / tempo de execução não permitir facilmente determinar a fonte da exceção, é possível registrá-lo imediatamente. C ++ e alguns mecanismos JS não expõem o arquivo + a linha ou a pilha de chamadas da exceção no momento em que você a capturou / mas essas informações estão disponíveis no momento em que você cria a exceção.

Nossa solução foi fornecer um mecanismo que usasse a configuração de tempo de execução para permitir o registro do tipo da exceção junto com uma pilha barata ao tentar diagnosticar esses problemas.

JBRWilkinson
fonte
+1 Isso é definitivamente um bom caso para registro no lance ... PHP prevê um rastreamento de pilha completo no prendedor embora, então eu provavelmente vou ir para o outro lado ...
yannis