Arquitectura de varias capas: ¿dónde debo implementar el registro / manejo de errores?

17

Actualmente estoy refactorizando un subsistema grande con una arquitectura de varias capas, y estoy luchando por diseñar una estrategia efectiva de registro / manejo de errores.

Digamos que mi arquitectura consta de las siguientes tres capas:

  • Interfaz pública (es decir, un controlador MVC)
  • Capa de dominio
  • Capa de acceso a datos

Mi fuente de confusión es donde debería implementar el registro de errores \ manejo:

  1. La solución más fácil sería implementar el registro en el nivel superior (es decir, la Interfaz pública \ Controlador MVC). Sin embargo, esto se siente mal porque significa aumentar la excepción a través de las diferentes capas y luego iniciar sesión; en lugar de registrar la excepción en su origen.

  2. Obviamente, registrar la excepción en su origen es la mejor solución porque tengo la mayor cantidad de información. Mi problema con esto es que no puedo capturar todas las excepciones en el origen sin capturar TODAS las excepciones, y en la capa de dominio / interfaz pública, esto conducirá a capturar excepciones que ya se han capturado, registrado y relanzado por la capa a continuación .

  3. Otra posible estrategia es una combinación de # 1 y # 2; por el cual capturo excepciones específicas en la capa que tienen más probabilidades de ser lanzadas (IE Catching, logging y re-throwing SqlExceptionsen Data Access Layer) y luego registro cualquier excepción no detectada en el nivel superior. Sin embargo, esto también requeriría que detecte y registre cada excepción en el nivel superior, porque no puedo distinguir entre los errores que ya se han registrado \ manejado y los que no.

Ahora, obviamente, este es un problema en la mayoría de las aplicaciones de software, por lo que debe haber una solución estándar para este problema que dé como resultado que las excepciones se detecten en el origen y se registren una vez; Sin embargo, no puedo ver cómo hacerlo yo mismo.

Tenga en cuenta que el título de esta pregunta es muy similar a ' Excepciones de registro en una aplicación de varios niveles " ', sin embargo, las respuestas en esa publicación carecen de detalles y no son suficientes para responder a mi pregunta.

KidCode
fuente
1
Un enfoque que a veces uso es crear mi propia subclase Exception (o RuntimeException) y lanzarla, incluida la excepción original como anidada. Por supuesto, significa que al capturar excepciones en los niveles superiores necesito verificar el tipo de excepción (mis propias excepciones se vuelven a lanzar, otras excepciones se registran e incluyen en nuevas instancias de mis excepciones). Pero he estado trabajando solo durante mucho tiempo, así que no puedo dar consejos "oficiales".
SJuan76
44
The easiest solution would be to implement the logging at the top level- hacer esto. Las excepciones de registro en su origen no son una buena idea y cada aplicación que he encontrado que hace esto fue una PITA para depurar. Debería ser responsabilidad de la persona que llama manejar las excepciones.
Justin
Parece que tiene en mente alguna técnica / lenguaje de implementación específico; de lo contrario, su declaración "las excepciones que se registraron no se pueden distinguir de las que no lo fueron" es difícil de entender. ¿Puedes dar más contexto?
Vroomfondel
@Vroomfondel: tienes razón. Estoy usando C #, y envolver el código en cada Capa con el try{ ... } catch(Exception ex) { Log(ex); }resultado resultaría en la misma excepción que se registra en cada capa. (También parece una práctica bastante mala detectar todas las excepciones en cada capa de la base de código).
KidCode el

Respuestas:

18

A sus preguntas:

La solución más fácil sería implementar el registro en el nivel superior

Tener la burbuja de excepción hasta el nivel superior es un enfoque absolutamente correcto y plausible. Ninguno de los métodos de capa superior intenta continuar algún proceso después de la falla, que generalmente no puede tener éxito. Y una excepción bien equipada contiene toda la información necesaria para iniciar sesión. Y no hacer nada con respecto a las excepciones lo ayuda a mantener su código limpio y enfocado en la tarea principal en lugar de las fallas.

Obviamente, registrar la excepción en su origen es la mejor solución porque tengo la mayor cantidad de información.

Eso es medio correcto. Sí, la información más útil está disponible allí. Pero recomendaría poner todo esto en el objeto de excepción (si aún no está allí) en lugar de registrarlo inmediatamente. Si inicia sesión en un nivel bajo, aún necesita lanzar una excepción para informar a las personas que llaman que no completó su trabajo. Esto termina en múltiples registros del mismo evento.

Excepciones

Mi directriz principal es capturar y registrar excepciones solo en el nivel superior. Y todas las capas a continuación deben asegurarse de que toda la información necesaria sobre fallas se transporte al nivel superior. Dentro de una aplicación de proceso único, por ejemplo, en Java, esto significa principalmente no intentar / capturar o iniciar sesión fuera del nivel superior.

A veces, desea que se incluya información de contexto en el registro de excepciones que no está disponible en la excepción original, por ejemplo, la instrucción SQL y los parámetros que se ejecutaron cuando se lanzó la excepción. Luego puede capturar la excepción original y volver a lanzar una nueva, que contenga la original más el contexto.

Por supuesto, la vida real a veces interfiere:

  • En Java, a veces debe capturar una excepción y envolverla en un tipo de excepción diferente solo para obedecer algunas firmas de métodos fijos. Pero si vuelve a lanzar una excepción, asegúrese de que la relanzada contenga toda la información necesaria para un registro posterior.

  • Si está cruzando un borde entre procesos, a menudo técnicamente no puede transferir el objeto de excepción completo, incluido el seguimiento de la pila. Y, por supuesto, la conexión podría perderse. Así que aquí hay un punto en el que un servicio debe registrar excepciones y luego hacer todo lo posible para transmitir la mayor cantidad posible de información de fallas a través de la línea a su cliente. El servicio debe asegurarse de que el cliente reciba un aviso de falla, ya sea al recibir una respuesta a la falla o al encontrarse con un tiempo de espera en caso de una conexión interrumpida. Por lo general, esto provocará que se registre la misma falla dos veces, una dentro del servicio (con más detalles) y otra en el nivel superior del cliente.

Inicio sesión

Estoy agregando algunas oraciones sobre el registro en general, no solo el registro de excepciones.

Además de situaciones excepcionales, también desea que las actividades importantes de su aplicación se registren en el registro. Entonces, use un marco de registro.

Tenga cuidado con los niveles de registro (¡leer registros donde la información de depuración y los errores graves no están marcados con diferentes en consecuencia es un dolor!). Los niveles de registro típicos son:

  • ERROR: algunas funciones fallaron irrecuperablemente. Eso no necesariamente significa que todo su programa se bloqueó, pero alguna tarea no se pudo completar. Por lo general, tiene un objeto de excepción que describe el error.
  • ADVERTENCIA: sucedió algo extraño, pero no se produjo ningún error en la tarea (se detectó una configuración extraña, una falla de conexión temporal que causó algunos reintentos, etc.)
  • INFORMACIÓN: Desea comunicar algunas acciones importantes del programa al administrador del sistema local (comenzar algún servicio con su configuración y versión de software, importar archivos de datos a la base de datos, usuarios que inician sesión en el sistema, se le ocurre la idea ...).
  • DEPURACIÓN: Cosas que como desarrollador desea ver cuando está depurando algún problema (pero nunca sabrá de antemano qué es lo que realmente necesita en caso de este o aquel error específico; si puede preverlo, lo solucionará) ) Una cosa que siempre es útil es registrar actividades en interfaces externas.

En producción, establezca el nivel de registro en INFO. Los resultados deberían ser útiles para un administrador del sistema para que sepa lo que está sucediendo. Espere que lo llame para pedir ayuda o corregir errores por cada ERROR en el registro y la mitad de las ADVERTENCIAS.

Habilite el nivel de DEPURACIÓN solo durante las sesiones de depuración reales.

Agrupe las entradas de registro en categorías apropiadas (por ejemplo, por el nombre de clase completo del código que genera la entrada), lo que le permite activar registros de depuración para partes específicas de su programa.

Ralf Kleberhoff
fuente
Gracias por una respuesta tan detallada, realmente aprecio la parte de registro también.
KidCode
-1

Me estoy preparando para los votos negativos, pero voy a ponerme nervioso y decir que no estoy seguro de poder estar de acuerdo con esto.

El aumento de las excepciones, mucho menos registrarlas nuevamente, es un esfuerzo adicional con poco beneficio. Capture la excepción en el origen (sí, lo más fácil), regístrela, pero luego no vuelva a lanzar la excepción, solo informe el "error" a la persona que llama. "-1", nulo, cadena vacía, alguna enumeración, lo que sea. La persona que llama solo necesita saber que la llamada falló, casi nunca los detalles horripilantes. Y esos estarán en su registro, ¿verdad? En el raro caso de que la persona que llama necesita los detalles, continúe y burbujee, pero no como un defecto automático irreflexivo.

Incondicionalmente Reinstalar Monica
fuente
3
El problema al informar el error mediante valores de retorno es: 1. Si la persona que llama se preocupa por las fallas, debe verificar el valor especial. Pero espera, ¿es nullo la cadena vacía? ¿Es -1 o algún número negativo? 2. Si a la persona que llama no le importa (es decir, no verifica), esto lleva a errores de seguimiento no relacionados con la causa original, por ejemplo a NullPointerException. O peor: el cálculo continúa con valores incorrectos. 3. Si a la persona que llama le importaría pero el programador no piensa que este método está fallando, el compilador no se lo recuerda. Las excepciones no tienen este problema, ya sea que atrapes o vuelves a lanzar.
siegi
1
Lo siento, tuve que rechazar eso. El enfoque que describe (reemplace las excepciones con valores de retorno especiales) fue de vanguardia en la década de 1970, cuando se originó el lenguaje C, y hay muchas buenas razones por las cuales los lenguajes modernos tienen excepciones, y para mí la principal es que El uso de excepciones correctamente hace que sea mucho más fácil escribir código robusto. Y su "Excepción de las burbujas en aumento [...] es un esfuerzo adicional" [...] es simplemente erróneo: simplemente no haga nada con respecto a las excepciones, y ellas surgirán por sí solas.
Ralf Kleberhoff