¿Por qué muchos mensajes de excepción no contienen detalles útiles?

213

Parece que hay una cierta cantidad de acuerdo en que los mensajes de excepción deberían contiene detalles útiles .

¿Por qué es que muchas excepciones comunes de los componentes del sistema no contienen detalles útiles?

Algunos ejemplos:

  • .NET List index access ArgumentOutOfRangeException not no me dice el valor del índice que se intentó y no fue válido, ni tampoco el rango permitido.
  • Básicamente, todos los mensajes de excepción de la biblioteca estándar de MSVC C ++ son completamente inútiles (en el mismo sentido que anteriormente).
  • Excepciones de Oracle en .NET, que le dicen (parafraseado) "TABLA O VISTA no encontrada", pero no cuál una.

Por lo tanto, a mí me parece que, en su mayor parte, los mensajes de excepción no contienen detalles suficientes para ser útiles. ¿Mis expectativas están fuera de línea? ¿Estoy usando las excepciones mal que incluso me doy cuenta de esto? O tal vez mi impresión sea incorrecta: la mayoría de las excepciones ¿ realmente proporcionan detalles útiles?

    
pregunta Martin Ba 13.04.2015 - 16:21

9 respuestas

202

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 correctamente.

Sí, IndexOutOfRangeException debe contener el índice exacto 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 .NET que no lo hace Sí, la excepción table or view not found de Oracle debe contener el nombre de la tabla o vista que no se encontró, y nuevamente, el hecho de que no sea 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 humanos, lo que a su vez se debe a una falta de comprensión de lo que se trata las excepciones, por lo que es un círculo vicioso.

Dado que la gente piensa que la excepción debe contener un mensaje legible para el ser humano, cree que cualquier información que se aporte en la excepción también debe tener el formato del mensaje legible para el hombre, y entonces están aburridos de escribir todo el texto del mensaje. código de creación de mensajes legibles, o temen que al hacerlo se esté divulgando una cantidad de información no aconsejable a cualquier persona que vea el mensaje. (Los problemas de seguridad mencionados en otras respuestas).

Pero la verdad del asunto es que no deberían preocuparse por eso porque la excepción no debería no contener un mensaje legible para los 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, eso debe hacerse 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 clase de la excepción , y cualquier otra información pertinente a la excepción debe copiarse en las variables miembro (final / readonly) del objeto de excepción. Preferiblemente, cada pequeña parte concebible de ella. De esta manera, ningún mensaje debe (o debería) generarse, y por lo tanto, ningún ojo indiscreto puede verlo.

Para abordar la preocupación expresada por Thomas Owens en un comentario a continuación:

Sí, por supuesto, en algún nivel, creará un mensaje de registro relacionado con 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 quiere permitir que el usuario vea todo el seguimiento de pila de excepción. Nuevamente, nuestro problema aquí es que nuestra perspectiva está sesgada por las prácticas tradicionales. Los archivos de registro tradicionalmente han estado en texto sin formato, lo que puede haber estado bien mientras nuestra disciplina estaba en su infancia, pero quizás ya no: si existe un problema de seguridad, entonces el archivo de registro debe ser binario y / o encriptado.

Ya sea en formato binario o en texto plano, el archivo de registro debe considerarse como una secuencia en la que la aplicación serializa la información de depuración. Dicha secuencia 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 la secuencia 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 indicado, es para todos los propósitos prácticos "el mensaje",) cada una de las variables de miembro de excepción que describen todo lo que es pertinente. e-práctico-para-incluir-en-un-registro, y todo el seguimiento de la pila. Tenga en cuenta que el formato de un mensaje de excepción legible para el ser humano falta notoriamente falta en este proceso.

P.S.

Algunos de mis pensamientos más sobre este tema se pueden encontrar en esta respuesta: Cómo escribir un buen mensaje de excepción

P.P.S.

Parece que a mucha gente le molestó mi sugerencia acerca de los archivos de registro binarios, así que enmendé la respuesta una vez más para dejar más claro que lo que estoy sugiriendo aquí es no que el archivo de registro debería ser binario, pero que el archivo de registro puede ser binario, si es necesario.

    
respondido por el Mike Nakis 13.04.2015 - 18:13
45
  

¿Por qué es que muchas excepciones comunes de los componentes del sistema no contienen detalles útiles?

En mi experiencia, hay varias 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.

  • Las personas enfocadas en la seguridad ven las excepciones como una fuente de fuga de información (para ejemplo ). Dado que el comportamiento predeterminado de las excepciones es mostrar esa información al usuario, los programadores a veces cometen errores por el lado de la precaución.
  • En C ++, he escuchado argumentos contra la asignación de memoria en bloques catch (donde se encuentran al menos parte del contexto para hacer buenos mensajes). Esa asignación es difícil de administrar y, lo que es peor, puede causar una excepción de falta de memoria allí: a menudo se bloquea la aplicación o se pierde la memoria. Es difícil dar formato a las excepciones sin asignar memoria, y esa práctica puede haber migrado de un idioma a otro como lo hacen los programadores.
  • No lo saben. Quiero decir, hay son escenarios en los que el código tiene no idea de lo que salió mal. Si el código no lo sabe, no se lo puede decir.
  • He trabajado en lugares donde los problemas de localización evitaron que solo se incluyeran cadenas en inglés en el sistema, incluso para las excepciones que solo leerían los miembros del personal de soporte que hablan inglés.
  • En algunos lugares, he visto excepciones que se utilizan más como aserciones. Están allí para proporcionar un mensaje claro y fuerte durante el desarrollo de que algo no se hizo, o que se hizo un cambio en un lugar, pero no en otro. Estos a menudo son lo suficientemente únicos como para que un buen mensaje sea un esfuerzo duplicado o simplemente confuso.
  • La gente es perezosa, los programadores más que la mayoría. Pasamos mucho menos tiempo en el camino excepcional que el camino feliz, y esto es un efecto secundario.
  

¿Mis expectativas están fuera de línea? ¿Estoy usando Excepciones de forma incorrecta que incluso me doy cuenta de esto?

Kinda? 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 escribir código para manejar condiciones excepcionales (ignorando el mensaje), no usándolos como una especie de mecanismo de retroalimentación interactivo al codificar. Es inevitable usarlos para propósitos de depuración, pero en la mayoría de las situaciones se deben mantener al mínimo. Si observa que este problema me preocupa que no esté haciendo un trabajo lo suficientemente bueno como para prevenirlos.

    
respondido por el Telastyn 13.04.2015 - 17:54
12

No tengo un exceso de experiencia con 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á corregirlo con facilidad, pero de manera realista, en aplicaciones grandes con múltiples clases que pueden generar una gran variedad de tipos de excepciones. , o el tipo de excepciones mismas , siempre es más valioso escribir su propia salida para la devolución de errores que confiar en el mensaje predeterminado.

Así es como debería ser, porque como muchas personas han señalado, algunas aplicaciones no quieren mostrar 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 producirse 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á, ya que 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.

    
respondido por el Zibbobz 13.04.2015 - 19:13
7

La pregunta es, específicamente, por qué las "excepciones de los sistemas 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 tampoco se hacen públicos documentos de diseño detallado u otras razones de diseño. En otras palabras, puede que nunca lo sepamos con seguridad.

Sin embargo, hay dos puntos clave a tener en cuenta por qué la información detallada de la excepción puede no ser conveniente o importante:

  1. Se puede usar una excepción llamando al código de cualquier manera: una biblioteca estándar no puede poner restricciones sobre cómo se usa la excepción. Específicamente, puede ser mostrado al usuario. Considere un índice de matriz fuera de límites: esto puede dar 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, una aplicación web o de escritorio), por lo que omitir la información puede ser más seguro desde una perspectiva de seguridad.

  2. Las excepciones no deben mostrarse al usuario. En su lugar, muestre un mensaje de error sencillo y registre la excepción en una ubicación a la que un atacante no tenga acceso (si corresponde). Una vez que se identifica el error, un desarrollador debe realizar una depuración a través del 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 podría esperar tener una excepción.

respondido por el user22815 13.04.2015 - 17:41
4

En primer lugar, permítame explotar una burbuja diciendo que incluso si el mensaje diag está cargado con información que lo lleva a la línea de código y al subcomando en 4 segundos, es probable que los usuarios nunca lo escriban o lo transmitan a la gente de soporte y se te dirá "Bueno, dijo algo acerca de una violación ... ¡No sé que parecía complicado!"

He estado escribiendo software y respaldando los resultados de otros por más de 30 años, 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 se ajusten los resultados finales. su modelo del universo, mucho más que ver con el hecho de que muchos en nuestra industria originalmente fueron autodidactas, y simplemente nunca incluyeron lecciones en comunicaciones. Tal vez si obligáramos a todos los codificadores nuevos a una posición de mantenimiento durante un par de años en los que tuvieran que lidiar con descubrir qué fue lo que no funcionó, entenderían la importancia de al menos algún tipo de precisión.

Recientemente, en una aplicación que se está reconstruyendo, se tomó la decisión de que los códigos de retorno se clasificarían en uno de los tres grupos:

  • 0 a 9 sería éxito a través de éxito con adicional información.
  • 10 a 99 no serían fatales (recuperables) errores, y
  • 101 a 255 serían errores fatales.

(100 por alguna razón se dejó de lado)

En cualquier flujo de trabajo en particular, nuestro pensamiento fue reutilizado o el código genérico usaría devoluciones genéricas (> 199) para errores fatales, lo que nos deja con 100 errores fatales posibles para un flujo de trabajo. Con datos leves de diferenciación en el mensaje, los 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 INDIVIDUAL fue el código de retorno 101.

Todo lo que se consideró, creo que la respuesta a su pregunta es que los mensajes carecen de significado porque cuando se crearon originalmente, debían ser marcadores de posición a los que nadie respondió. Finalmente, la gente descubrió cómo solucionar los problemas, no por los mensajes, sino por desecharlos.

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 intentan pasarlo al soporte (he visto mensajes de error que fueron cortados y pegados por el uso que luego fueron eliminados antes de ser enviados con el comentario posterior que Parecía mucha información y posiblemente no podría quererla toda, por lo que eliminaron un montón al azar.

Y seamos realistas, con demasiados (no casi 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 código de error / retorno, me parece que en algún lugar de los módulos ejecutados debería haber una línea que diga algo como "si la condición devuelve el valor-código" y la condición debería decirle por qué el código de retorno se produjo. Esto parece un enfoque lógico simple, pero por mi vida, simplemente TRATE de que Microsoft le cuente lo que sucedió cuando una actualización de Windows falló en el CÓDIGO 80241013 o algún otro identificador único. Un poco triste, ¿no?

    
respondido por el Randy 14.04.2015 - 08:52
3

Si bien estoy de acuerdo con que las excepciones deben contener la mayor cantidad de información 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 acerca de lo que estaba tratando de hacer en el lugar en el código donde recibió la excepción. Mientras que a menudo realmente no puede hacer mucho para corregir la situación cuando algo sale mal en una biblioteca fuera de su control, puede agregar información mucho más útil en qué situación ha salido mal.

En el caso de que la tabla no se encuentre, no será de mucha ayuda si se le dice que la tabla que no se puede encontrar se llama ESTUDIANTES, porque no tiene dicha tabla y esa cadena no está en ninguna parte de su código.

Pero si detecta la excepción y la vuelve a generar con la instrucción SQL que estaba tratando de ejecutar, estará mejor, ya que resultó que intentó insertar un registro con el campo de nombre Robert '); DROP TABLE ESTUDIANTES; (¡Siempre hay un xkcd!)

Por lo tanto, para combatir las excepciones que no son informativas: try-catch-rethrow con más información específica sobre lo que intentabas hacer.

Probablemente debería agregar, para que esto sea más una respuesta al por qué de 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 sé por qué se ha intentado algo que falló, esa lógica está en el código de llamada.

    
respondido por el Bent 14.04.2015 - 10:52
3

Para dar una respuesta ligeramente diferente: el código ofensivo probablemente se ha hecho a especificación:

  • La función recibe X y devuelve Y
  • Si X no es válida, lanza la excepción Z

Agregue la presión de entregar exactamente a especificación (por temor a ser rechazado en revisión / prueba) en un tiempo mínimo y con un mínimo esfuerzo, entonces tiene su receta para una excepción de biblioteca totalmente inútil y que no es útil.

    
respondido por el Stu Pegg 14.04.2015 - 23:12
3

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 el marco de llamada y capturar el marco de llamada, y eso es costoso. Por lo tanto, los programadores no desean usar las excepciones mucho.

En Ocaml, el lanzamiento de excepciones es casi tan rápido como un C setjmp (su costo no depende del número de tramas de llamadas cruzadas), por lo que los desarrolladores pueden usarlo mucho (incluso para casos no excepcionales, muy comunes) , casos). En contraste, las excepciones de C ++ son lo suficientemente intensas, 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 se puede "detener" 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.

Por lo tanto, dependiendo del idioma (y de los hábitos de los desarrolladores que lo usan), una excepción podría llevar muchos detalles útiles, o por el contrario, ser usada como un salto rápido no local y llevar solo los datos muy útiles.

    
respondido por el Basile Starynkevitch 14.04.2015 - 09:03
-2

¿Qué te hace pensar que el valor del índice o el rango requerido o el nombre de la tabla es un detalle útil, para una excepción?

Las excepciones no son un mecanismo de manejo de errores; son un mecanismo de recuperación .

El punto de excepción es aumentar hasta el nivel de código que puede manejar la excepción. Donde sea que se encuentre ese nivel, o bien 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 adecuado.

La única vez que puedo pensar en dónde podría ser útil la información adicional podría es en el nivel superior absoluto de su aplicación en la que falla y emite un volcado de errores; pero no es el trabajo de la excepción acceder, compilar y almacenar esta información.

No estoy diciendo que puedes poner "lanzar una nueva excepción" en todas partes y decir que es bueno, es posible escribir malas excepciones. Pero incluir información irrelevante no es necesario para usarlos correctamente.

    
respondido por el Odalrick 15.04.2015 - 13:13

Lea otras preguntas en las etiquetas