Parece que há uma certa concordância de que as mensagens de exceção devem conter detalhes úteis .
Por que muitas exceções comuns dos componentes do sistema não contêm detalhes úteis?
Alguns exemplos:
- NET
List
acesso de índiceArgumentOutOfRangeException
que não me dizer o valor do índice que foi julgado e era inválida, nem me diga o intervalo permitido. - Basicamente, todas as mensagens de exceção da biblioteca padrão do MSVC C ++ são totalmente inúteis (na mesma linha acima).
- Exceções do Oracle no .NET, informando (parafraseado) "TABLE OR VIEW não encontrado", mas não qual .
Então, para mim, parece que na maioria das vezes as mensagens de exceção não contêm detalhes suficientes para serem úteis. Minhas expectativas estão fora de linha? Estou usando exceções erradas para perceber isso? Ou talvez a minha impressão é errada: a maioria das exceções que realmente fornecer detalhes úteis?
c#
c++
exceptions
Martin Ba
fonte
fonte
Respostas:
As exceções não contêm detalhes úteis porque o conceito de exceções ainda não amadureceu na disciplina de engenharia de software; muitos programadores não as entendem completamente e, portanto, não as tratam adequadamente.
Sim,
IndexOutOfRangeException
deve conter o índice preciso que estava fora do intervalo, bem como o intervalo válido no momento em que foi lançado, e é desprezível em nome dos criadores do tempo de execução do .NET que não. Sim, atable or view not found
exceção da Oracle deve conter o nome da tabela ou exibição que não foi encontrada e, novamente, o fato de não existir é desprezível em nome de quem é responsável por isso.Em grande parte, a confusão decorre da idéia original equivocada de que as exceções devem conter mensagens legíveis por humanos, que, por sua vez, decorrem da falta de compreensão do que são essas exceções; portanto, é um ciclo vicioso.
Como as pessoas pensam que a exceção deve conter uma mensagem legível por humanos, elas acreditam que qualquer informação transmitida pela exceção também deve ser formatada na mensagem legível por humanos e, em seguida, ficam entediadas em escrever todas as mensagens legíveis por humanos - código de construção, ou eles temem que isso possa estar divulgando uma quantidade desaconselhável de informações para qualquer olhar curioso que possa ver a mensagem. (Os problemas de segurança mencionados por outras respostas.)
Mas a verdade é que eles não devem se preocupar com isso, porque a exceção não deve conter uma mensagem legível por humanos. Exceções são coisas que somente os programadores devem ver e / ou lidar. Se houver a necessidade de apresentar informações de falha a um usuário, isso deve ser feito em um nível muito alto, de maneira sofisticada e no idioma do usuário, que, estatisticamente falando, é improvável que seja inglês.
Portanto, para nós programadores, a "mensagem" da exceção é o nome da classe da exceção , e qualquer outra informação pertinente à exceção deve ser copiada nas variáveis de membro (final / somente leitura) do objeto de exceção. De preferência, cada pedacinho concebível disso. Dessa forma, nenhuma mensagem precisa (ou deve) ser gerada e, portanto, nenhum olhar curioso pode vê-la.
Para abordar a preocupação expressa por Thomas Owens em um comentário abaixo:
Sim, claro que, em algum nível, você vai criar uma mensagem de log sobre a exceção. Mas você já vê o problema do que está dizendo: por um lado, uma mensagem de log de exceção sem rastreamento de pilha é inútil, mas, por outro lado, não deseja permitir que o usuário veja todo o rastreamento de pilha de exceção. Novamente, nosso problema aqui é que nossa perspectiva é distorcida pelas práticas tradicionais. Os arquivos de log tradicionalmente estão em texto simples, o que pode ter sido bom enquanto nossa disciplina ainda estava em sua infância, mas talvez não mais: se houver um problema de segurança, o arquivo de log deverá ser binário e / ou criptografado.
Seja binário ou texto sem formatação, o arquivo de log deve ser considerado como um fluxo no qual o aplicativo serializa as informações de depuração. Esse fluxo seria apenas para os olhos dos programadores, e a tarefa de gerar informações de depuração para uma exceção deve ser tão simples quanto serializar a exceção no fluxo de log de depuração. Dessa forma, observando o log, você verá o nome da classe de exceção (que, como já afirmei, é para todos os fins práticos "a mensagem"), cada uma das variáveis de membro de exceção que descrevem tudo o que é pertinente. e prático de incluir em um log e todo o rastreamento de pilha. Observe como a formatação de uma mensagem de exceção legível por humanos está conspicuamente ausente desse processo.
PS
Mais alguns dos meus pensamentos sobre esse assunto podem ser encontrados nesta resposta: Como escrever uma boa mensagem de exceção
PPS
Parece que um monte de pessoas estavam sendo incomodada por minha sugestão sobre arquivos de log binário, então eu alterada a resposta, mais uma vez para torná-lo ainda mais claro que o que eu estou sugerindo aqui é não que o arquivo de log deve ser binário, mas que o arquivo de log pode ser binário, se necessário.
fonte
Na minha experiência, existem várias razões pelas quais as exceções não contêm informações úteis. Espero que esse tipo de razão também se aplique aos componentes do sistema - mas não tenho certeza.
Meio? Quero dizer, as exceções devem ter boas mensagens, mas também são exceções . Você deve gastar seu tempo projetando código para evitar condições excepcionais ou escrevendo código para lidar com condições excepcionais (ignorando a mensagem), não os utilizando como uma espécie de mecanismo de feedback interativo ao codificar. É inevitável usá-los para fins de depuração, mas na maioria das situações isso deve ser mantido no mínimo. O fato de você perceber esse problema me preocupa que você não esteja fazendo um trabalho bom o suficiente para evitá-los.
fonte
Eu não tenho excesso de experiência em C # ou especificamente em C ++, mas posso lhe dizer isso - as exceções escritas pelo desenvolvedor 9 em 10 vezes são mais úteis do que qualquer exceção genérica que você encontrará, ponto final.
Idealmente, sim, uma exceção genérica indicará exatamente por que o erro ocorreu e você poderá corrigi-lo com facilidade - mas de maneira realista, em aplicativos grandes com várias classes que podem gerar uma ampla variedade de tipos diferentes de exceções ou o Mesmo tipo de exceções, é sempre mais valioso gravar sua própria saída para retorno de erro do que confiar na mensagem padrão.
É assim que deve ser, porque, como muitas pessoas apontaram, alguns aplicativos não desejam lançar uma mensagem de erro que não desejam que o usuário veja, por motivos de segurança ou para evitar confundi-los.
Em vez disso, você deve prever em seu design quais tipos de erros podem ser gerados no seu aplicativo (e sempre haverá erros) e escrever mensagens de captura de erros que o ajudarão a identificar o problema.
Isso nem sempre ajudará você - porque você nem sempre pode prever qual mensagem de erro será útil - mas é o primeiro passo para entender melhor seu próprio aplicativo a longo prazo.
fonte
A questão está perguntando especificamente por que tantas exceções geradas por "componentes do sistema" (também conhecidas como classes de biblioteca padrão) não contêm detalhes úteis.
Infelizmente, a maioria dos desenvolvedores não grava os componentes principais nas bibliotecas padrão, nem os documentos detalhados do projeto ou outras justificativas do projeto são necessariamente tornados públicos. Em outras palavras, talvez nunca tenhamos certeza.
No entanto, há dois pontos principais a serem lembrados sobre o motivo pelo qual as informações detalhadas das exceções podem não ser desejáveis ou importantes:
Uma exceção pode ser usada chamando código de qualquer maneira: uma biblioteca padrão não pode restringir a maneira como a exceção é usada. Especificamente, ele pode ser exibido para o usuário. Considere um índice de matriz fora dos limites: isso pode fornecer informações úteis a um invasor. O designer de idiomas não tem idéia de como um aplicativo usará a exceção lançada ou mesmo que tipo de aplicativo é (por exemplo, aplicativo Web ou desktop); portanto, deixar de fora as informações pode ser mais segura do ponto de vista da segurança.
Exceções não devem ser exibidas para o usuário. Em vez disso, exiba uma mensagem de erro amigável e registre a exceção em um local que um invasor não tem acesso (se aplicável). Depois que o erro é identificado, o desenvolvedor deve depurar o código, inspecionando os quadros da pilha e os caminhos lógicos. Neste ponto, o desenvolvedor tem mais informações do que uma exceção jamais poderia ter.
fonte
Primeiro, deixe-me estourar uma bolha dizendo que, mesmo que a mensagem diag seja carregada com informações que o levem à linha de código exata e ao subcomando em 4 segundos, é provável que os usuários nunca anotem ou transmitam para o pessoal de suporte e você será informado "Bem, isso disse algo sobre uma violação ... eu não sei, parecia complicado!"
Estou escrevendo software e dando suporte aos resultados de outros softwares há mais de 30 anos e, pessoalmente, a qualidade atual de uma mensagem de exceção praticamente não tem nada a ver com segurança, independentemente de como os resultados finais se ajustaram ao modelo deles. o universo, muito mais a ver com o fato de que muitos em nossa indústria eram originalmente autodidatas, e eles nunca incluíam lições de comunicação. Talvez se forçássemos todos os novos codificadores em uma posição de manutenção por alguns anos, nos quais eles precisariam lidar com o que havia de errado, eles entenderiam a importância de pelo menos alguma forma de precisão.
Recentemente, em um aplicativo sendo reconstruído, foi tomada uma decisão de que os códigos de retorno se enquadrariam em um dos três grupos:
(100 foi excluído por algum motivo)
Em qualquer fluxo de trabalho específico, nosso pensamento foi reutilizado ou o código genérico usaria retornos genéricos (> 199) para erros fatais, deixando-nos 100 erros fatais possíveis para um fluxo de trabalho. Com alguns dados diferenciadores na mensagem, todos os erros como o arquivo não encontrado podem usar o mesmo código e diferenciar-se pelas mensagens.
Quando o código voltou dos contratados, você não acreditaria em nossa surpresa quando praticamente TODO O ERRO FATAL ÚNICO fosse o código de retorno 101.
Tudo o que considero que acho que a resposta para sua pergunta é que as mensagens são tão sem sentido porque, quando criadas originalmente, deveriam ser espaços reservados aos quais ninguém voltou. Eventualmente, as pessoas descobriram como corrigir problemas, não por causa das mensagens, mas DISPITE-AS.
Desde aquela época, o pessoal autodidata simplesmente nunca teve um bom exemplo do que uma exceção deveria conter. Acrescente a isso que mais usuários não leem as mensagens, muito menos tentam repassá-las para o suporte (vi mensagens de erro que foram recortadas e coladas pelos usados que foram depois editadas antes de serem enviadas com o comentário posterior de que parecia muita informação e eu não poderia querer tudo, então eles removeram aleatoriamente um monte delas.
E vamos enfrentá-lo, com muitos (não todos, mas muitos) da próxima geração de codificadores, se for mais trabalhoso e não adicionar flash, apenas não vale a pena ...
Última observação: se uma mensagem de erro incluir um código de erro / retorno, parece-me que em algum lugar dos módulos executados deve haver uma linha que lê algo como "se a condição retornar código-valor" e a condição deve informar por que o código de retorno ocorreu. Parece uma abordagem lógica simples, mas, para a minha vida, apenas tente que a Microsoft conte o que aconteceu quando uma atualização do Windows falhou no CODE 80241013 ou em algum outro identificador muito exclusivo. Meio triste, não é?
fonte
chances are the users will never write it down... and you will be told "Well it said something about a violation..."
É por isso que você usa uma ferramenta de log de exceção para gerar automaticamente o relatório de erro contendo o rastreamento de pilha e possivelmente até enviá-lo ao seu servidor. Eu tive um usuário uma vez que não era muito técnico. Toda vez que ela enviava uma mensagem do registrador de erros, era algo como "Não sei o que fiz de errado, mas ...", não importa quantas vezes expliquei que esse erro significava que o bug estava do meu lado . Mas sempre recebi os relatórios de erros dela!Embora eu concorde que as exceções devem conter o máximo de informações possível, ou pelo menos ser menos genérico. No caso da tabela não encontrada, o nome da tabela seria bom.
Mas você sabe mais sobre o que estava tentando fazer no local do código em que recebeu a exceção. Embora muitas vezes você realmente não possa fazer muito para corrigir a situação quando algo der errado em uma biblioteca fora do seu controle, você poderá adicionar informações muito mais úteis sobre a situação em que algo deu errado.
No caso de tabela não encontrada, isso não ajudará muito se você for informado de que a tabela que não pode ser encontrada se chama STUDENTS, porque você não possui uma tabela e essa string não está em nenhum lugar do seu código.
Mas se você pegar a exceção e a repetir novamente com a instrução SQL que estava tentando executar, ficará melhor, pois você tentou inserir um registro com o campo de nome Robert '); ESTUDANTES DA TABELA DE GOTA; (Sempre existe um xkcd!)
Portanto, para combater as exceções menos informativas: tente pegar e relançar com mais informações específicas do que você estava tentando fazer.
Provavelmente devo acrescentar, para que seja mais uma resposta do porquê da pergunta, que uma provável razão pela qual o foco dos criadores das bibliotecas não tenha sido o de melhorar as mensagens de exceção é que eles não sabem por que algo foi tentado que falhou, essa lógica está no código de chamada.
fonte
throw_with_nested
muito.Para dar uma resposta um pouco diferente: O código incorreto provavelmente foi feito para especificar:
Adicione a pressão para entregar exatamente as especificações (por medo de ser rejeitado em revisão / teste) em tempo mínimo e com o mínimo de confusão, e você terá sua receita para uma exceção de biblioteca totalmente compatível e inútil.
fonte
As exceções têm um custo específico ao idioma e à implementação.
Por exemplo, são necessárias exceções em C ++ para destruir todos os dados vivos entre o quadro de chamada de lançamento e o quadro de captura de chamada, e isso é caro. Portanto, os programadores não desejam usar muito as exceções.
No Ocaml, o lançamento de exceções é quase tão rápido quanto um C
setjmp
(seu custo não depende do número de quadros de chamadas atravessadas), para que os desenvolvedores possam usá-lo muito (mesmo em casos não excepcionais, muito comuns). Por outro lado, as exceções do C ++ são pesadas o suficiente, então você provavelmente não as usará como no Ocaml.Um exemplo típico é uma pesquisa ou exploração recursiva que pode ser "interrompida" dentro de uma recursão bastante profunda (por exemplo, encontre uma folha em uma árvore ou uma função de unificação). Em alguns idiomas, é mais rápido (mais idiomático) propagar essa condição para todos os chamadores. Em outros idiomas, lançar uma exceção é mais rápido.
Portanto, dependendo do idioma (e dos hábitos dos desenvolvedores), uma exceção pode conter muitos detalhes úteis ou, pelo contrário, ser usada como um salto não local rápido e apenas os dados muito úteis.
fonte
O que faz você pensar que o valor do índice, o intervalo necessário ou o nome da tabela são um detalhe útil, por uma exceção?
Exceções não são um mecanismo de tratamento de erros; eles são um mecanismo de recuperação .
O ponto das exceções é aumentar o nível de código que pode lidar com a exceção. Onde quer que esse nível esteja, você tem as informações necessárias ou elas não são relevantes. Se houver informações de que você precisa, mas não possui acesso imediato , você não está manipulando a exceção no nível apropriado.
A única vez em que consigo pensar em onde informações extras podem ser úteis é no nível superior absoluto do seu aplicativo, onde você trava e emite um erro de despejo; mas não é o trabalho da exceção acessar, compilar e armazenar essas informações.
Não estou dizendo que você pode colocar "lançar nova exceção" em todos os lugares e chamar de bom, é possível escrever exceções ruins. Mas incluir informações irrelevantes não é necessário para usá-las adequadamente.
fonte