Solución:
Throwable.printStackTrace()
escribe el seguimiento de la pila en System.err
PrintStream. los System.err
flujo y el flujo de salida de “error” estándar subyacente del proceso JVM puede ser redirigido por
- invocando
System.setErr()
que cambia el destino apuntado porSystem.err
. - o redirigiendo el flujo de salida de error del proceso. La secuencia de salida de error puede redirigirse a un archivo / dispositivo
- cuyo contenido puede ser ignorado por el personal,
- Es posible que el archivo / dispositivo no sea capaz de rotar el registro, lo que infiere que es necesario reiniciar el proceso para cerrar el identificador de archivo / dispositivo abierto, antes de archivar el contenido existente del archivo / dispositivo.
- o el archivo / dispositivo descarta todos los datos escritos en él, como es el caso de
/dev/null
.
Inferir de lo anterior, invocar Throwable.printStackTrace()
constituye un comportamiento de manejo de excepciones válido (no bueno / excelente), solo
- Si tu no tienes
System.err
ser reasignados a lo largo de la vida útil de la aplicación, - y si no necesita la rotación de registros mientras se ejecuta la aplicación,
- y si la práctica de registro aceptada / diseñada de la aplicación es escribir a
System.err
(y el flujo de salida de error estándar de la JVM).
En la mayoría de los casos, no se cumplen las condiciones anteriores. Es posible que uno no sepa que se está ejecutando otro código en la JVM, y no se puede predecir el tamaño del archivo de registro o la duración del tiempo de ejecución del proceso, y una práctica de registro bien diseñada giraría en torno a la escritura de archivos de registro “analizables por máquina” (un característica preferible pero opcional en un registrador) en un destino conocido, para ayudar en el soporte.
Por último, conviene recordar que la salida de Throwable.printStackTrace()
definitivamente se intercalaría con otro contenido escrito para System.err
(y posiblemente incluso System.out
si ambos se redirigen al mismo archivo / dispositivo). Esta es una molestia (para aplicaciones de un solo subproceso) con la que uno debe lidiar, ya que los datos sobre excepciones no se pueden analizar fácilmente en tal evento. Peor aún, es muy probable que una aplicación de subprocesos múltiples produzca registros muy confusos como Throwable.printStackTrace()
no es seguro para subprocesos.
No hay ningún mecanismo de sincronización para sincronizar la escritura del seguimiento de la pila en System.err
cuando se invocan varios subprocesos Throwable.printStackTrace()
al mismo tiempo. Resolver esto en realidad requiere que su código se sincronice en el monitor asociado con System.err
(y también System.out
, si el archivo / dispositivo de destino es el mismo), y ese es un precio bastante alto a pagar por la cordura del archivo de registro. Para tomar un ejemplo, el ConsoleHandler
y StreamHandler
Las clases son responsables de agregar registros de registro a la consola, en la función de registro proporcionada por java.util.logging
; la operación real de publicación de registros de registro está sincronizada: cada hilo que intente publicar un registro de registro también debe adquirir el bloqueo en el monitor asociado con el StreamHandler
ejemplo. Si desea tener la misma garantía de tener registros de registro no intercalados utilizando System.out
/System.err
, debe asegurarse de lo mismo: los mensajes se publican en estos flujos de manera serializable.
Teniendo en cuenta todo lo anterior y los escenarios muy restringidos en los que Throwable.printStackTrace()
es realmente útil, a menudo resulta que invocarlo es una mala práctica.
Ampliando el argumento en uno de los párrafos anteriores, también es una mala elección para usar Throwable.printStackTrace
junto con un registrador que escribe en la consola. Esto se debe en parte a la razón por la que el registrador se sincronizaría en un monitor diferente, mientras que su aplicación (posiblemente, si no desea registros de registro intercalados) se sincronizaría en un monitor diferente. El argumento también es válido cuando usa dos registradores diferentes que escriben en el mismo destino, en su aplicación.
Estás tocando varios temas aquí:
1) Un seguimiento de pila nunca debe ser visible para los usuarios finales (por motivos de seguridad y experiencia del usuario)
Sí, debería ser accesible para diagnosticar problemas de los usuarios finales, pero el usuario final no debería verlos por dos razones:
- Son muy oscuros e ilegibles, la aplicación se verá muy poco amigable para el usuario.
- Mostrar un seguimiento de pila al usuario final podría presentar un riesgo de seguridad potencial. Corríjame si me equivoco, PHP en realidad imprime los parámetros de función en el seguimiento de la pila, brillante, pero muy peligroso, si obtiene una excepción al conectarse a la base de datos, ¿qué es probable que haga en el seguimiento de la pila?
2) Generar un seguimiento de pila es un proceso relativamente costoso (aunque es poco probable que sea un problema en la mayoría de las circunstancias “excepcionales”)
La generación de un seguimiento de pila ocurre cuando se crea / lanza la excepción (es por eso que lanzar una excepción tiene un precio), la impresión no es tan costosa. De hecho, puede anular Throwable#fillInStackTrace()
en su excepción personalizada, lo que hace que lanzar una excepción sea casi tan barato como una simple declaración GOTO.
3) Muchos marcos de registro imprimirán el seguimiento de la pila por usted (el nuestro no lo hace y no, no podemos cambiarlo fácilmente)
Muy buen punto. El problema principal aquí es: si el marco registra la excepción por usted, no haga nada (¡pero asegúrese de que lo haga!) Si desea registrar la excepción usted mismo, use el marco de registro como Logback o Log4J, para no ponerlos en la consola sin formato. porque es muy difícil controlarlo.
Con el marco de registro, puede redirigir fácilmente los seguimientos de la pila a un archivo, consola o incluso enviarlos a una dirección de correo electrónico específica. Con codificado printStackTrace()
tienes que vivir con el sysout
.
4) La impresión del seguimiento de la pila no constituye un tratamiento de errores. Debe combinarse con otro registro de información y manejo de excepciones.
De nuevo: log SQLException
correctamente (con el seguimiento de la pila completa, utilizando el marco de registro) y mostrar agradable: “Lo sentimos, actualmente no podemos procesar su solicitud“. ¿De verdad crees que el usuario está interesado en las razones? ¿Has visto la pantalla de error de StackOverflow? Es muy gracioso, pero no revela alguna detalles. Sin embargo, asegura al usuario que se investigará el problema.
Pero él voluntad lo llame de inmediato y debe poder diagnosticar el problema. Por lo tanto, necesita ambos: un registro de excepciones adecuado y mensajes fáciles de usar.
Para terminar: siempre log excepciones (preferiblemente utilizando un marco de registro), pero no las exponga al usuario final. Piense con cuidado y sobre los mensajes de error en su GUI, muestre los rastros de pila solo en el modo de desarrollo.
Lo primero printStackTrace () no es caro como usted afirma, porque el seguimiento de la pila se completa cuando se crea la excepción.
La idea es pasar todo lo que vaya a los registros a través de un marco de registro, de modo que se pueda controlar el registro. Por lo tanto, en lugar de usar printStackTrace, simplemente use algo como Logger.log(msg, exception);