Puedo ver varias publicaciones donde se enfatizó la importancia de manejar la excepción en la ubicación central o en el límite del proceso como una buena práctica en lugar de ensuciar cada bloque de código alrededor de try / catch. Creo firmemente que la mayoría de nosotros entendemos la importancia de esto, sin embargo, veo que la gente todavía termina con el patrón anti captura-registro-relanzamiento principalmente porque para facilitar la resolución de problemas durante cualquier excepción, desean registrar más información específica del contexto (ejemplo: parámetros del método pasado) y la forma es envolver el método alrededor de try / catch / log / rethrow.
public static bool DoOperation(int num1, int num2)
{
try
{
/* do some work with num1 and num2 */
}
catch (Exception ex)
{
logger.log("error occured while number 1 = {num1} and number 2 = {num2}");
throw;
}
}
¿Hay una manera correcta de lograr esto mientras se mantienen las buenas prácticas de manejo de excepciones? Escuché sobre el marco de AOP como PostSharp para esto, pero me gustaría saber si hay algún inconveniente o un costo de rendimiento importante asociado con estos marcos de AOP.
¡Gracias!
fuente
Respuestas:
El problema no es el bloqueo local, el problema es el registro y la repetición . Maneje la excepción o envuélvala con una nueva excepción que agregue contexto adicional y tírela. De lo contrario, se encontrará con varias entradas de registro duplicadas para la misma excepción.
La idea aquí es mejorar la capacidad de depurar su aplicación.
Ejemplo # 1: manejarlo
Si maneja la excepción, puede degradar fácilmente la importancia de la entrada del registro de excepciones y no hay razón para filtrar esa excepción en la cadena. Ya está solucionado.
Manejar una excepción puede incluir informar a los usuarios que ocurrió un problema, registrar el evento o simplemente ignorarlo.
NOTA: si ignora intencionalmente una excepción, le recomiendo que proporcione un comentario en la cláusula catch vacía que indique claramente por qué. Esto les permite a los futuros mantenedores saber que no fue un error o una programación perezosa. Ejemplo:
Ejemplo # 2: Agregar contexto adicional y lanzar
Agregar contexto adicional (como el número de línea y el nombre del archivo en el código de análisis) puede ayudar a mejorar la capacidad de depurar archivos de entrada, suponiendo que el problema esté ahí. Este es un caso especial, por lo que volver a empaquetar una excepción en una "ApplicationException" solo para cambiar el nombre no le ayuda a depurar. Asegúrese de agregar información adicional.
Ejemplo 3: no hagas nada con la excepción
En este caso final, solo permite que la excepción se vaya sin tocarla. El controlador de excepciones en la capa más externa puede manejar el registro. La
finally
cláusula se utiliza para asegurarse de que se limpien los recursos necesarios para su método, pero este no es el lugar para registrar que se produjo la excepción.fuente
No creo que las capturas locales sean un antipatrón, de hecho, si recuerdo correctamente, ¡en realidad se aplica en Java!
Lo que es clave para mí al implementar el manejo de errores es la estrategia general. Es posible que desee un filtro que capture todas las excepciones en el límite del servicio, puede interceptarlas manualmente; ambas están bien siempre que haya una estrategia general que se ajuste a los estándares de codificación de sus equipos.
Personalmente, me gusta detectar errores dentro de una función cuando puedo hacer uno de los siguientes:
Si no es uno de estos casos, no agrego un try / catch local. Si es así, dependiendo del escenario, puedo manejar la excepción (por ejemplo, un método TryX que devuelve un falso) o volver a lanzar para que la estrategia global maneje la excepción.
Por ejemplo:
O un ejemplo de repensar:
Luego, detecta el error en la parte superior de la pila y presenta un mensaje agradable al usuario.
Independientemente del enfoque que adopte, siempre vale la pena crear pruebas unitarias para estos escenarios para que pueda asegurarse de que la funcionalidad no cambie e interrumpa el flujo del proyecto en una fecha posterior.
No ha mencionado en qué idioma está trabajando, pero es un desarrollador de .NET y lo ha visto demasiadas veces sin mencionarlo.
NO ESCRIBA:
Utilizar:
¡El primero restablece el rastro de la pila y hace que su nivel superior de captura sea completamente inútil!
TLDR
La captura local no es un antipatrón, a menudo puede ser parte de un diseño y puede ayudar a agregar contexto adicional al error.
fuente
Esto depende mucho del idioma. Por ejemplo, C ++ no ofrece rastreos de pila en mensajes de error de excepción, por lo que puede ser útil rastrear la excepción a través de capturas, registros y repeticiones frecuentes. Por el contrario, Java y lenguajes similares ofrecen muy buenos seguimientos de pila, aunque el formato de estos seguimientos de pila puede no ser muy configurable. Capturar y volver a generar excepciones en estos lenguajes no tiene ningún sentido a menos que realmente pueda agregar algún contexto importante (por ejemplo, conectar una excepción SQL de bajo nivel con el contexto de una operación de lógica de negocios).
Cualquier estrategia de manejo de errores que se implemente a través de la reflexión es casi necesariamente menos eficiente que la funcionalidad integrada en el lenguaje. Además, el registro generalizado tiene una sobrecarga de rendimiento inevitable. Por lo tanto, debe equilibrar el flujo de información que obtiene con otros requisitos para este software. Dicho esto, las soluciones como PostSharp que se basan en instrumentación a nivel de compilador generalmente funcionarán mucho mejor que la reflexión en tiempo de ejecución.
Personalmente, creo que registrar todo no es útil, porque incluye mucha información irrelevante. Por lo tanto, soy escéptico de las soluciones automatizadas. Dado un buen marco de registro, podría ser suficiente tener una directriz de codificación acordada que discuta qué tipo de información le gustaría registrar y cómo se debe formatear esta información. Luego puede agregar el registro donde sea importante.
Iniciar sesión en la lógica empresarial es mucho más importante que iniciar sesión en las funciones de utilidad. Y la recopilación de trazas de la pila de informes de fallas del mundo real (que solo requiere el registro en el nivel superior de un proceso) le permite ubicar áreas del código donde el registro tendría el mayor valor.
fuente
Cuando veo
try/catch/log
en cada método, surge la preocupación de que los desarrolladores no tenían idea de lo que podría suceder o no en su aplicación, asumieron lo peor y registraron preventivamente todo en todas partes debido a todos los errores que esperaban.Este es un síntoma de que las pruebas de unidad e integración son insuficientes y los desarrolladores están acostumbrados a pasar por un montón de código en el depurador y esperan que de alguna manera una gran cantidad de registros les permita implementar código defectuoso en un entorno de prueba y encontrar los problemas observando los registros.
El código que arroja excepciones puede ser más útil que el código redundante que captura y registra excepciones. Si lanza una excepción con un mensaje significativo cuando un método recibe un argumento inesperado (y lo registra en el límite del servicio) es mucho más útil que registrar inmediatamente la excepción lanzada como un efecto secundario del argumento no válido y tener que adivinar qué lo causó .
Los nulos son un ejemplo. Si obtiene un valor como argumento o el resultado de una llamada a un método y no debe ser nulo, arroje la excepción. No solo registre el resultado resultante
NullReferenceException
cinco líneas más tarde debido al valor nulo. De cualquier manera, obtienes una excepción, pero una te dice algo mientras que la otra te hace buscar algo.Como han dicho otros, es mejor registrar excepciones en el límite del servicio, o cada vez que una excepción no se vuelve a lanzar porque se maneja con gracia. La diferencia más importante es entre algo y nada. Si sus excepciones se registran en un lugar de fácil acceso, encontrará la información que necesita cuando la necesita.
fuente
Si necesita registrar información de contexto que aún no está en la excepción, envuélvala en una nueva excepción y proporcione la excepción original como
InnerException
. De esa manera, aún conserva el rastro original de la pila. Entonces:El segundo parámetro para el
Exception
constructor proporciona una excepción interna. Luego, puede registrar todas las excepciones en un solo lugar y aún así obtener el seguimiento completo de la pila y la información contextual en la misma entrada de registro.Es posible que desee utilizar una clase de excepción personalizada, pero el punto es el mismo.
try / catch / log / rethrow es un desastre porque conducirá a registros confusos, por ejemplo, ¿qué sucede si ocurre una excepción diferente en otro hilo entre el registro de la información contextual y el registro de la excepción real en el controlador de nivel superior? Sin embargo, try / catch / throw está bien, si la nueva excepción agrega información al original.
fuente
La excepción en sí misma debe proporcionar toda la información necesaria para un registro adecuado, incluido el mensaje, el código de error y lo que no. Por lo tanto, no debería haber necesidad de detectar una excepción solo para volver a lanzarla o lanzar una excepción diferente.
A menudo verá el patrón de varias excepciones capturadas y relanzadas como una excepción común, como la captura de una excepción DatabaseConnectionException, una InvalidQueryException y una InvalidSQLParameterException y volver a lanzar una DatabaseException. Aunque para esto, diría que todas estas excepciones específicas deberían derivarse de DatabaseException en primer lugar, por lo que no es necesario volver a lanzarlas.
Descubrirá que eliminar las cláusulas innecesarias de prueba de captura (incluso las que tienen fines de registro) en realidad facilitará el trabajo, no lo hará más difícil. Solo los lugares en su programa que manejan la excepción deben registrar la excepción y, si todo lo demás falla, un controlador de excepciones para todo el programa para un intento final de registrar la excepción antes de salir con gracia del programa. La excepción debe tener un seguimiento completo de la pila que indique el punto exacto en el que se produjo la excepción, por lo que a menudo no es necesario proporcionar un registro de "contexto".
Dicho esto, AOP puede ser una solución de solución rápida para usted, aunque generalmente implica una ligera desaceleración en general. Le animo a que, en su lugar, elimine las cláusulas innecesarias de try catch por completo cuando no se agrega nada de valor.
fuente
try { tester.test(); } catch (NullPointerException e) { logger.error("Variable tester was null!"); }
. El seguimiento de la pila es suficiente en la mayoría de los casos, pero al carecer de eso, el tipo de error suele ser adecuado.