Parece que hay cierto acuerdo en que los mensajes de excepción deben contener detalles útiles .
¿Por qué es que muchas excepciones comunes de los componentes del sistema no contienen detalles útiles?
Algunos ejemplos:
- El
List
acceso al índice .NETArgumentOutOfRangeException
no me dice el valor del índice que se intentó y no era válido, ni me dice el rango permitido. - Básicamente, todos los mensajes de excepción de la biblioteca estándar MSVC C ++ son completamente inútiles (en la misma línea que arriba).
- Excepciones de Oracle en .NET, que le dicen (parafraseado) "TABLA O VISTA no encontrada", pero no cuál .
Entonces, para mí parece que, en su mayor parte, los mensajes de excepción no contienen detalles suficientes para ser útiles. ¿Están mis expectativas fuera de línea? ¿Estoy usando excepciones equivocadas que incluso me doy cuenta de esto? O tal vez mi impresión es incorrecto: la mayoría de las excepciones no realmente ofrecen detalles útiles?
c#
c++
exceptions
Martin Ba
fuente
fuente
Respuestas:
Las excepciones no contienen detalles útiles porque el concepto de excepciones aún no ha madurado lo suficiente dentro de la disciplina de ingeniería de software, por lo que muchos programadores no las entienden completamente y, por lo tanto, no las tratan adecuadamente.
Sí,
IndexOutOfRangeException
debe contener el índice preciso que estaba fuera del rango, así como el rango que era válido en el momento en que se lanzó, y es despreciable en nombre de los creadores del tiempo de ejecución de .NET que no lo hace. Sí, latable or view not found
excepción de Oracle debe contener el nombre de la tabla o vista que no se encontró, y nuevamente, el hecho de que no lo haga es despreciable en nombre de quien sea responsable de esto.En gran parte, la confusión se deriva de la idea original equivocada de que las excepciones deben contener mensajes legibles por los humanos, lo que a su vez se debe a la falta de comprensión de qué se tratan las excepciones, por lo que es un círculo vicioso.
Dado que las personas piensan que la excepción debe contener un mensaje legible por humanos, creen que cualquier información que contenga la excepción también debe formatearse en el mensaje legible por humanos, y luego se aburren de escribir todo el mensaje legible por humanos. código de construcción, o tienen miedo de que hacerlo pueda estar divulgando una cantidad de información desaconsejable a los ojos curiosos que puedan ver el mensaje. (Los problemas de seguridad mencionados por otras respuestas).
Pero la verdad del asunto es que no deberían preocuparse por eso porque la excepción no debe contener un mensaje legible por humanos. Las excepciones son cosas que solo los programadores deberían ver y / o tratar. Si alguna vez es necesario presentar información de fallas a un usuario, debe hacerlo a un nivel muy alto, de manera sofisticada y en el idioma del usuario, que, estadísticamente hablando, es poco probable que sea inglés.
Entonces, para nosotros los programadores, el "mensaje" de la excepción es el nombre de la clase de la excepción , y cualquier otra información pertinente a la excepción debe copiarse en las variables miembro (final / solo lectura) del objeto de excepción. Preferiblemente, cada pequeño fragmento concebible. De esta manera, no es necesario (o debería) generar un mensaje, y por lo tanto, ningún ojo curioso puede verlo.
Para abordar la preocupación expresada por Thomas Owens en un comentario a continuación:
Sí, por supuesto, en algún nivel, se va a crear un mensaje de registro con respecto a la excepción. Pero ya ve el problema con lo que está diciendo: por un lado, un mensaje de registro de excepción sin un seguimiento de pila es inútil, pero por otro lado, no desea permitir que el usuario vea el seguimiento completo de la pila de excepción. Nuevamente, nuestro problema aquí es que nuestra perspectiva está sesgada por las prácticas tradicionales. Los archivos de registro han estado tradicionalmente en texto plano, lo que puede haber estado bien mientras nuestra disciplina estaba en su infancia, pero tal vez ya no: si existe un problema de seguridad, entonces el archivo de registro debe ser binario y / o encriptado.
Ya sea texto binario o simple, el archivo de registro debe considerarse como una secuencia en la que la aplicación serializa la información de depuración. Tal flujo sería solo para los ojos de los programadores, y la tarea de generar información de depuración para una excepción debería ser tan simple como serializar la excepción en el flujo de registro de depuración. De esta manera, al mirar el registro puede ver el nombre de la clase de excepción (que, como ya he dicho, es para todos los propósitos prácticos "el mensaje"), cada una de las variables miembro de excepción que describen todo lo pertinente. y-práctico-para-incluir-en-un-registro, y todo el seguimiento de la pila. Observe cómo el formato de un mensaje de excepción legible por humanos falta notablemente en este proceso.
PD
Algunos de mis pensamientos sobre este tema se pueden encontrar en esta respuesta: Cómo escribir un buen mensaje de excepción
PPS
Parece que una gran cantidad de personas estaban siendo enumeró por mi sugerencia acerca de los archivos de registro binario, por lo que modificó la respuesta una vez más para que sea aún más claro que lo que estoy sugiriendo aquí es no que el archivo de registro debe ser binarios, pero eso el archivo de registro puede ser binario, si es necesario.
fuente
En mi experiencia, hay una serie de razones por las cuales las excepciones no contienen información útil. Espero que este tipo de razones también se apliquen a los componentes del sistema, pero no estoy seguro.
Un poco? Quiero decir, las excepciones deberían tener buenos mensajes, pero también son excepciones . Debería pasar su tiempo diseñando código para evitar condiciones excepcionales, o escribiendo código para manejar condiciones excepcionales (ignorando el mensaje), no usándolos como una especie de mecanismo interactivo de retroalimentación al codificar. Es inevitable usarlos con fines de depuración, pero en la mayoría de las situaciones se debe mantener al mínimo. Que te des cuenta de este problema me preocupa que no estés haciendo un buen trabajo para prevenirlos.
fuente
No tengo un exceso de experiencia en C #, o C ++ específicamente, pero puedo decirte esto: las excepciones escritas por el desarrollador 9 de cada 10 veces son más útiles que cualquier excepción genérica que puedas encontrar, punto.
Idealmente, sí, una excepción genérica le indicará exactamente por qué ocurrió el error y podrá solucionarlo con facilidad, pero de manera realista, en aplicaciones grandes con múltiples clases que pueden generar una amplia variedad de diferentes tipos de excepciones, o Con el mismo tipo de excepciones, siempre es más valioso escribir su propia salida para la devolución de errores que confiar en el mensaje predeterminado.
Así es como debe ser, porque como muchas personas han señalado, algunas aplicaciones no quieren lanzar un mensaje de error que no quieren que el usuario vea, por razones de seguridad o para evitar confundirlos.
En su lugar, debe anticipar en su diseño qué tipos de errores pueden aparecer en su aplicación (y siempre habrá errores) y escribir mensajes de detección de errores que lo ayuden a identificar el problema.
Esto no siempre lo ayudará, porque no siempre puede anticipar qué mensaje de error será útil, pero es el primer paso para comprender mejor su propia aplicación a largo plazo.
fuente
La pregunta es específicamente por qué tantas excepciones lanzadas por "componentes del sistema" (también conocidas como clases de biblioteca estándar) no contienen detalles útiles.
Desafortunadamente, la mayoría de los desarrolladores no escriben los componentes principales en las bibliotecas estándar, ni los documentos de diseño detallados u otra razón de diseño necesariamente se hacen públicos. En otras palabras, es posible que nunca lo sepamos con certeza.
Sin embargo, hay dos puntos clave a tener en cuenta en cuanto a por qué la información de excepción detallada podría no ser deseable o importante:
Se puede usar una excepción llamando al código de cualquier manera: una biblioteca estándar no puede imponer restricciones sobre cómo se usa la excepción. Específicamente, se puede mostrar al usuario. Considere un índice de matriz fuera de límites: esto puede proporcionar información útil a un atacante. El diseñador de idiomas no tiene idea de cómo una aplicación usará la excepción lanzada o incluso qué tipo de aplicación es (por ejemplo, aplicación web o escritorio), por lo que omitir información puede ser más seguro desde una perspectiva de seguridad.
No se deben mostrar excepciones al usuario. En su lugar, muestre un mensaje de error descriptivo y registre la excepción en una ubicación a la que el atacante no tenga acceso (si corresponde). Una vez que se identifica el error, un desarrollador debe depurar el código, inspeccionando los marcos de pila y las rutas lógicas. En este punto, el desarrollador tiene más información de la que una excepción podría esperar tener.
fuente
En primer lugar, permítanme reventar una burbuja diciendo que incluso si el mensaje de diagnóstico está cargado con información que lo lleva a la línea de código y al subcomando exactos en 4 segundos, es probable que los usuarios nunca lo escriban ni lo transmitan a la gente de soporte y se le dirá "Bueno, dijo algo sobre una violación ... ¡No sé si parecía complicado!"
Llevo más de 30 años escribiendo software y respaldando los resultados de otros programas, y personalmente, la calidad actual de un mensaje de excepción no tiene prácticamente nada que ver con la seguridad, independientemente de cómo los resultados finales se ajustan a su modelo de el universo, mucho más que ver con el hecho de que muchos en nuestra industria fueron originalmente autodidactas, y simplemente nunca incluyeron lecciones en comunicaciones. Tal vez si FORZAMOS a todos los codificadores nuevos a una posición de mantenimiento durante un par de años donde tuvieron que lidiar con la resolución de lo que salió mal, comprenderían la importancia de al menos alguna forma de precisión.
Recientemente, en una aplicación que se está reconstruyendo, se tomó la decisión de que los códigos de retorno caigan en uno de tres grupos:
(100 por alguna razón quedó fuera)
En cualquier flujo de trabajo en particular, nuestro pensamiento se reutilizó o el código genérico usaría retornos genéricos (> 199) para errores fatales, dejándonos con 100 errores fatales posibles para un flujo de trabajo. Con datos ligeramente diferenciales en el mensaje, errores como el archivo no encontrado podrían usar el mismo código y diferenciarse con los mensajes.
Cuando el código regresó de los contratistas, no creería nuestra sorpresa cuando prácticamente CADA ERROR FATAL SOLO fue el código de retorno 101.
Todo lo que consideró, creo que la respuesta a su pregunta es que los mensajes no tienen sentido porque cuando se crearon originalmente, debían ser marcadores de posición a los que nadie volvía. Finalmente, la gente descubrió cómo solucionar los problemas no por los mensajes, sino por DISPITARLOS.
Desde ese momento, la gente autodidacta simplemente nunca tuvo un buen ejemplo de lo que debería contener una excepción. Agregue a eso que más usuarios no leen los mensajes, y mucho menos tratan de pasarlo al soporte (he visto mensajes de error que fueron cortados y pegados por el usuario que luego fueron redactados antes de ser enviados con el comentario posterior de que Parecía una gran cantidad de información y no podía quererla toda, por lo que eliminaron al azar un montón de ella.
Y seamos sinceros, con demasiados (no todos pero demasiados) de la próxima generación de codificadores, si es más trabajo y no agrega flash, simplemente no vale la pena ...
Última nota: si un mensaje de error incluye un error / código de retorno, me parece que en algún lugar de los módulos ejecutados debería haber una línea que diga algo así como "si la condición devuelve el valor-código" y la condición debería decirle por qué el código de retorno ocurrió. Esto parece un enfoque lógico simple, pero para mi vida, solo INTENTE hacer que Microsoft le diga qué sucedió cuando una actualización de Windows falló en el CÓDIGO 80241013 o algún otro identificador único. Un poco triste, ¿no?
fuente
chances are the users will never write it down... and you will be told "Well it said something about a violation..."
Es por eso que utiliza una herramienta de registro de excepciones para generar automáticamente el informe de error que contiene el seguimiento de la pila y posiblemente incluso enviarlo a su servidor. Una vez tuve un usuario que no era muy técnico. Cada vez que enviaba un mensaje del registrador de errores, decía algo como "No estoy seguro de qué hice mal, pero ...", no importa cuántas veces le expliqué que este error significaba que el error estaba de mi lado . ¡Pero siempre recibí los informes de error de ella!Si bien estoy de acuerdo en que las excepciones deben contener tanta información como sea posible, o al menos ser menos genéricas. En el caso de la tabla no encontrada, el nombre de la tabla sería bueno.
Pero sabe más sobre lo que estaba tratando de hacer en el lugar del código donde recibió la excepción. Si bien a menudo no puede hacer mucho para rectificar la situación cuando algo sale mal en una biblioteca fuera de su control, puede agregar mucha más información útil sobre en qué situación algo salió mal.
En el caso de la tabla no encontrada, no le ayudará mucho si le dicen que la tabla que no se puede encontrar se llama ESTUDIANTES, porque no tiene una tabla de ese tipo, y esa cadena no está en ninguna parte de su código.
Pero si detecta la excepción y la vuelve a lanzar con la instrucción SQL que intentaba ejecutar, será mejor, ya que resulta que intentó insertar un registro con el campo de nombre Robert '); ESTUDIANTES DE TABLA DE GOTA; (¡Siempre hay un xkcd!)
Por lo tanto, para combatir las excepciones menos informativas: trate de atrapar y volver a lanzar con más información específica de lo que estaba tratando de hacer.
Probablemente debería agregar, para que esto sea más una respuesta al por qué en la pregunta, que una razón probable por la que los creadores de las bibliotecas no se han centrado en mejorar los mensajes de excepción es que no saben por qué se intentó algo que falló, esa lógica está en el código de llamada.
fuente
throw_with_nested
mucho.Para dar una respuesta ligeramente diferente: El código ofensivo probablemente se haya hecho para especificar:
Agregue la presión para cumplir exactamente con las especificaciones (por temor a ser rechazado en la revisión / prueba) en un tiempo mínimo y con un mínimo de alboroto, entonces tiene su receta para una excepción de biblioteca totalmente compatible e inútil.
fuente
Las excepciones tienen un costo específico de idioma e implementación.
Por ejemplo, se requieren excepciones de C ++ para destruir todos los datos vivos entre lanzar un marco de llamada y capturar un marco de llamada, y eso es costoso. Por lo tanto, los programadores no desean utilizar muchas excepciones.
En Ocaml, el lanzamiento de excepciones es casi tan rápido como una C
setjmp
(su costo no depende de la cantidad de tramas de llamadas atravesadas), por lo que los desarrolladores pueden usarlo mucho (incluso para casos no excepcionales, muy comunes). Por el contrario, las excepciones de C ++ son lo suficientemente pesadas, por lo que probablemente no las usará mucho como lo haría en Ocaml.Un ejemplo típico es una búsqueda o exploración recursiva que puede "detenerse" dentro de una recursión bastante profunda (por ejemplo, encontrar una hoja en un árbol o una función de unificación). En algunos idiomas es más rápido (por lo tanto, más idiomático) propagar esa condición a cada persona que llama. En otros idiomas, lanzar una excepción es más rápido.
Entonces, dependiendo del idioma (y los hábitos de los desarrolladores que lo usan), una excepción podría llevar muchos detalles útiles o, por el contrario, usarse como un salto rápido no local y llevar solo los datos muy útiles.
fuente
¿Qué te hace pensar que el valor del índice o el rango requerido o el nombre de la tabla es un detalle útil, por una excepción?
Las excepciones no son un mecanismo de manejo de errores; Son un mecanismo de recuperación .
El punto de excepciones es aumentar hasta el nivel de código que puede manejar la excepción. Donde sea que esté ese nivel, tiene la información necesaria o no es relevante. Si hay información que necesita, pero no tiene acceso inmediato , no está manejando la excepción en el nivel apropiado.
La única vez que puedo pensar en dónde podría ser útil la información adicional es en el nivel superior absoluto de su aplicación, donde se bloquea y emite un volcado de error; pero no es el trabajo de la excepción acceder, compilar y almacenar esta información.
No estoy diciendo que puedas poner "lanzar una nueva Excepción" en todas partes y llamarlo bueno, es posible escribir malas excepciones. Pero incluir información irrelevante no es necesario para usarlos correctamente.
fuente