Ideas + Ingeniería del Software
Buenas prácticas: Tratamiento de Excepciones
Enero 13th, 2009 - [Enlace local]
Entrada rápida que resume algo que ya he escrito en otros sitios, y que me gustaría tener público (¡y recibir comentarios!).
Criterios a tener en cuenta al tratar excepciones en Java:
- Es mejor que la aplicación falle a no saber que ha fallado (y qué ha fallado). Corolario: si no vas a tratar una excepción correctamente, mejor no hagas nada y que la aplicación falle.
- Siempre que se produzca un error en la aplicación tiene que haber información sobre el error en pantalla, al menos indicando que se ha producido un error. Corolario: no te limites a trazar el error en el log, ¡no siempre habrá alguien mirando!
- No traces mediante System.out o System.err, utiliza un sistema de logging.
- Si sólo vas a relanzar la excepción, no la relances, haz que el método lance ese tipo de excepción.
- No loguees y relances constantemente, no es necesario. Los únicos puntos que no deben lanzar excepciones son los métodos invocados directamente por la aplicación, para que no le llegue al navegador la traza del error. Y si utilizas un framework que gestione bien las excepciones (como Seam), incluso puedes saltarte esta norma.
- Cuando escribas en el log, escribe información significativa, como los parámetros recibidos por el método. Lo trivial (el nombre del método, por ejemplo) ya se verá en la traza.
- Al trazar, no concatenes el mensaje de la excepción, vuelca la excepción en sí (toda la traza).
- Un método no debería declararse como throws Exception, ya que enmascara todas las excepciones, sin permitir gestionar el error concreto. No pasa nada, por ejemplo, por que lance varias: throws IOException, MiOtraException. De esta forma, el que la invoque sabrá a qué tipos de error se debe enfrentar.
- } catch(Exception e) { }; : se ocultan los errores, es imposible saber qué ocurre.
- } catch(Exception e) { System.err.println(e) }; : estamos trazando en err, ni se ve nada en pantalla ni en el log, así que en explotación no sabremos qué ocurre.
- } catch(Exception e) { log.debug("Ha ocurrido algo malo: "+e); }; : el nivel de log es incorrecto (debería ser error), no se dan datos sobre el error, estamos concatenando la excepción, no estamos relanzando...
- } catch(Exception e) { log.error(e) }; : no se dan datos sobre el error, y no estamos lanzando nada, así que en pantalla no se verán los errores.
- } catch(Exception e) { log.error("Identificador: "+id, e); throw new MiExcepcion(e) }; : trazamos el error (la excepción) e indicamos un parámetro que puede estar provocando el error. Además, lanzamos una excepción (que contiene la provocada, para no perder traza) para que el nivel superior pueda seguir tratando el error. Esto sería incorrecto, de todas formas, si en nuestro sistema esto vuelca la traza en el interfaz de usuario. Aparte, probablemente incluso sea innecesario hacer esto (ver último ejemplo correcto).
- } catch(Exception e) { log.error("Identificador: "+id, e); anhadirMensajeDeErrorAlUsuario(e) }; : trazamos el error (la excepción) e indicamos un parámetro que puede estar provocando el error. Además, mostramos un mensaje de error en pantalla. Esto sería incorrecto, de todas formas, si este método es invocado por un nivel que necesita procesar el error específicamente.
- ...) throws IOException : si el método no va a hacer nada interesante con la excepción, ¿para qué capturarla?