¿Es más sensato registrar excepciones en una clase general o en una clase de excepción base?

15

Estoy en el proceso de refactorizar una aplicación web bastante grande. Uno de los principales problemas es el manejo inconsistente de errores y estoy tratando de encontrar una estrategia sensata. He creado un controlador de errores personalizado, a través de set_error_handler que esencialmente convierte los errores de PHP en ErrorExceptions y una clase de excepción base personalizada, que hereda directamente de Exception .

En producción, estoy usando una excepción genérica general, a través de set_exception_handler , y estoy a punto de agregar el registro de excepciones * a la mezcla. Mi dilema es dónde hacer el registro real, en la clase de excepción base o en el conjunto general.

He pensado en un par de razones para iniciar sesión en el conjunto:

  • Hay bastantes excepciones en el código que deben convertirse a algún elemento secundario apropiado de la clase de excepción base. Hasta que eso suceda, no se registrarán todas las excepciones.
  • De alguna manera se siente más natural hacerlo en general, una clase de excepción básica no debería hacer más que ser solo eso. (Puede ser una cuestión de principio de responsabilidad única, pero podría ser un sentimiento equivocado)

y una razón para iniciar sesión en la clase de excepción base:

  • Actualmente, el catch-all solo se usa en la producción. Sería fácil introducirlo en nuestros otros entornos (desarrollo, pruebas), pero eso requeriría algunos ajustes, ya que los errores se manejan de manera diferente por entorno, ya que en la producción se traducen a páginas de error 404/503.

¿Existe alguna práctica aceptable sobre dónde registrar excepciones?

* El registro implicará escribir en un archivo de texto al principio, y puede evolucionar a enviar correos para ciertos tipos de excepciones.


Algunas aclaraciones, impulsadas por la respuesta de @ unholysampler :

Me enfrento a una base de código sloc 2 * 10 ^ 6, con muchas cosas de terceros sobre las que no tengo control, y parte del código que sí tengo control sobre excepciones anteriores a las fechas en PHP. Y también hay un código reciente horrible, nos estamos recuperando de un largo período de intensa presión donde prácticamente tuvimos que dejar de pensar y simplemente pirateamos.

Estamos refactorizando activamente para abordar todas las inconsistencias e introducir un enfoque sensible de manejo de errores, pero eso llevará algún tiempo. Estoy más interesado en qué hacer hasta que llegue al punto donde los errores se manejan adecuadamente. Probablemente haré otra pregunta sobre una estrategia de excepción sensata en algún momento.

La principal motivación detrás del registro es recibir un correo electrónico en mi teléfono cada vez que ocurre algo malo en la producción. No me importa si los volcados de datos se vuelven enormes, si lo hacen, tendré un trabajo cron eliminando los viejos de vez en cuando.

Yannis
fuente

Respuestas:

11

En resumen, el único momento en que debe registrar la existencia de una excepción es cuando lo está manejando.

Cuando lanza una excepción, es porque su código ha alcanzado un estado en el que no puede proceder correctamente. Al lanzar una excepción, está representando un mensaje específico para su programa sobre el error que ocurrió. No debe atrapar una excepción hasta que se encuentre en un punto donde pueda manejarse adecuadamente.

El código que escriba como parte de su aplicación principal debe tener en cuenta los tipos de excepciones que se pueden generar y cuándo se pueden generar. Si no puede hacer nada productivo con una excepción, no lo atrape. No registre una excepción hasta que se esté manejando. Solo el código de manejo sabe qué significa la excepción en el contexto del flujo del programa y cómo responder. Escribir un mensaje de registro aquí puede tener sentido aquí. Si usa un marco de registro, puede establecer un nivel de registro para el mensaje y potencialmente filtrarlo. Esto funciona bien para las excepciones que pueden ocurrir, pero que no son críticas y pueden recuperarse de manera limpia.

Su excepción general es su último esfuerzo para evitar que su código se estrelle con una muerte fea. Si ha llegado hasta aquí, registrará toda la información de estado y error que pueda. Luego, hace todo lo posible para decirle amablemente al usuario que el programa se bloquea antes de que todo se detenga. Su objetivo debe ser nunca tener este código ejecutado.

Incrustar el registro en la clase base no sigue las pautas anteriores. La clase base no sabe nada sobre el estado del código. (Tener el seguimiento de la pila no cuenta porque no va a escribir código que tome decisiones basadas en el análisis). La clase base no puede hacer nada para indicar la gravedad o cómo se podría manejar la excepción. No desea grandes volcados de datos y seguimientos de pila cada vez que hay una excepción simple que puede manejar y recuperar de forma limpia.

muestreador
fuente
Agregué algunas aclaraciones sobre la pregunta provocada por su respuesta. ¿De lo que deduzco, en el lado práctico de la pregunta que propone iniciar sesión en el general?
Yannis
1
@YannisRizos: Sí, debe implementar el principio general como primer paso. Lo que dije sobre el tema general fue más para garantizar que no lo usaste como una parte normal de tu flujo de código. La implementación de un controlador de excepciones no controlado es importante porque le permite obtener mucha información cada vez que su código hace algo mal.
unholysampler el
¿No hay marcos de registro que puedan manejar convenientemente el concepto de "Aquí hay un montón de cosas que deberían registrarse a menos que se reemplacen"? Cada capa que ve una excepción podría reemplazar los datos de la anterior, excepto que si se lanza una nueva excepción en el curso del desbobinado de la pila, el último informe de registro no se reemplazaría y, por lo tanto, se registraría. ¿No hay marcos compatibles con ese patrón?
supercat
3

Si su idioma / tiempo de ejecución no le permite determinar fácilmente el origen de la excepción, existe un caso para registrarlo en el lanzamiento. C ++ y algunos motores JS no exponen el archivo + línea o la pila de llamadas de la excepción en el momento en que la detecta / pero esta información está disponible en el momento en que construye la excepción.

Nuestra solución fue proporcionar un mecanismo que utilizara la configuración de tiempo de ejecución para permitir el registro del tipo de excepción junto con una pila barata cuando se intenta diagnosticar estos problemas.

JBRWilkinson
fuente
1 Eso es definitivamente un caso bueno para el registro en el tiro ... PHP proporciona una traza completa en la captura sin embargo, así que probablemente vaya a otro lado ...
Yannis