Quem deve ler Exception.Message?


27

Ao criar exceções, devo escrever mensagens que um usuário ou desenvolvedor deve entender? Quem realmente deve ser o leitor de mensagens de exceção?

Acho que as mensagens de exceção não são úteis e sempre tenho dificuldade em escrevê-las. Por convenção, o tipo de exceção já deve nos dizer por que algo não funcionou e as propriedades personalizadas podem adicionar ainda mais informações, como nomes de arquivos, índices, chaves etc. Então, por que repeti-lo na própria mensagem? Uma mensagem gerada automaticamente também pode fazer e tudo o que ela precisa conter é o nome da exceção com uma lista de propriedades adicionais. Isso seria exatamente tão útil quanto um texto manuscrito.

Não seria melhor não escrever mensagens, mas ter representantes de exceção especiais que cuidam da criação de mensagens significativas, talvez em idiomas diferentes, em vez de codificá-las no código?


Me perguntaram se uma dessas perguntas fornece uma resposta para minha pergunta:

Eu li os dois e não fiquei feliz com as respostas deles. Eles falam sobre usuários em geral e se concentram no conteúdo da mensagem em si, e não no destinatário, e pode haver pelo menos dois deles: o usuário final e o desenvolvedor. Nunca sei com quem devo falar ao escrever mensagens de exceção.

Até acho que a famosa mensagem não tem nenhum valor real, pois apenas repete o nome do tipo de exceção em palavras diferentes. Por que se preocupar em escrevê-las? Eu posso gerá-los perfeitamente automaticamente.

Para mim, a mensagem de exceção carece de diferenciação de seus leitores. Uma exceção perfeita precisaria fornecer pelo menos duas versões da mensagem: uma para o usuário final e outra para o desenvolvedor. Chamar apenas uma mensagem é muito genérico. Uma mensagem do desenvolvedor deve ser escrita em inglês, mas a mensagem do usuário final pode precisar ser traduzida para outros idiomas. Não é possível conseguir tudo isso com apenas uma mensagem; portanto, uma exceção precisaria fornecer algum identificador à mensagem do usuário final que, como acabei de dizer, pode estar disponível em diferentes idiomas.

Quando leio todas as outras perguntas vinculadas, tenho a impressão de que uma mensagem de exceção realmente deve ser lida por um usuário final e não por um desenvolvedor ... uma única mensagem é como comer um bolo e também comê-lo.


4
Há duas outras perguntas feitas aqui nos programadores que vêm à mente. Não tenho certeza se eles são duplicados ou não, mas acho que você deve lê-los e talvez eles atendam a algumas ou até todas as suas preocupações: como escrever uma boa mensagem de exceção e por que muitas mensagens de exceção não contêm detalhes úteis ?
Thomas Owens

2
@ThomasOwens Eu li os dois, mas eles não abordam o destinatário específico de uma mensagem de exceção. Eles falam sobre usuários em geral, mas quem é ele? É o usuário do software que agora tem idéia sobre programação ou desenvolvedores que deve analisar o que deu errado? Nunca tenho certeza de quem devo me dirigir ao escrever mensagens.
t3chb0t

2
Acho que uma boa resposta aqui pode se referir a uma ou a ambas as perguntas, mas elas estão longe de serem duplicadas.
Lightness Races com Monica

1
Por que diabos isso foi fechado como duplicado? Mesmo que tenha sido uma duplicata dessa outra pergunta (o que não é), essa claramente tem uma resposta melhor, deve ser a outra que está marcada como duplicada.
Ben Aaronson

2
@MichaelT Eu ainda realmente não vejo muita sobreposição lá a menos que você pressupõe que as mensagens de exceção são para usuários
Ben Aaronson

Respostas:


46

Essas mensagens são para outros desenvolvedores

Espera- se que essas mensagens sejam lidas pelos desenvolvedores para ajudá-los a depurar o aplicativo. Isso pode assumir duas formas:

  • Depuração ativa. Na verdade, você está executando um depurador enquanto escreve o código e tenta descobrir o que acontece. Nesse contexto, uma exceção útil o guiará, facilitando a compreensão do que está acontecendo de errado ou, eventualmente, sugerindo uma solução alternativa (embora isso seja opcional).

  • Depuração passiva. O código é executado na produção e falha. A exceção é registrada, mas você recebe apenas a mensagem e o rastreamento de pilha. Nesse contexto, uma útil mensagem de exceção o ajudará a localizar rapidamente o bug.

    Como essas mensagens geralmente são registradas, isso também significa que você não deve incluir informações confidenciais (como chaves privadas ou senhas, mesmo que possam ser úteis para depurar o aplicativo).

Por exemplo, o IOSecurityExceptiontipo de exceção lançada ao gravar um arquivo não é muito explícito sobre o problema: é porque não temos permissões para acessar um arquivo? Ou talvez possamos ler, mas não escrever? Ou talvez o arquivo não exista e não tenhamos permissão para criar arquivos lá? Ou talvez esteja bloqueado (espero que o tipo de exceção seja diferente nesse caso, mas, na prática, os tipos às vezes podem ser enigmáticos). Ou talvez o Code Access Security nos impeça de realizar qualquer operação de E / S?

Em vez de:

IOSecurityException: o arquivo foi encontrado, mas a permissão para ler seu conteúdo foi negada.

é muito mais explícito. Aqui, sabemos imediatamente que as permissões no diretório estão definidas corretamente (caso contrário, não poderemos saber que o arquivo existe), mas as permissões no nível do arquivo são problemáticas.

Isso também significa que, se você não puder fornecer nenhuma informação adicional que ainda não esteja no tipo da exceção, poderá manter a mensagem vazia. DivisionByZeroExceptioné um bom exemplo em que a mensagem é redundante. Por outro lado, o fato de a maioria dos idiomas permitir que você lance uma exceção sem especificar sua mensagem é feito por um motivo diferente: porque a mensagem padrão já está disponível ou porque será gerada mais tarde, se necessário (e a geração dessa A mensagem está entre o tipo de exceção, que faz todo o sentido, "OOPly" falando).

Observe que, por razões técnicas (geralmente de desempenho), algumas mensagens acabam sendo muito mais enigmáticas do que deveriam. .NET NullReferenceException:

Referência de objeto não definida para uma instância de um objeto.

é um excelente exemplo de mensagem que não é útil. Uma mensagem útil seria:

Referência de objeto productnão definida para uma instância de um objeto quando chamada product.Price.

Essas mensagens não são para usuários finais!

Os usuários finais não devem ver mensagens de exceção. Nunca. Embora alguns desenvolvedores acabem mostrando essas mensagens para os usuários, isso leva a uma má experiência e frustração do usuário. Mensagens como:

Referência de objeto não definida para uma instância de um objeto.

não significa absolutamente nada para o usuário final e deve ser evitado a todo custo.

O pior cenário é ter uma tentativa / captura global que lança a exceção para o usuário e sai do aplicativo. Um aplicativo que se preocupa com seus usuários:

  • Lida com exceções em primeiro lugar. A maioria deles pode ser manuseada sem incomodar o usuário. A rede está inoperante? Por que não esperar alguns segundos e tentar novamente?

  • Impede que um usuário leve o aplicativo a um caso excepcional. Se você pedir ao usuário para digitar dois números e dividir o primeiro pelo segundo, por que você deixaria o usuário digitar zero no segundo caso para culpá- lo alguns segundos depois? Que tal destacar a caixa de texto em vermelho (com uma dica útil de ferramenta informando que o número deve ser diferente de zero) e desativar o botão de validação até que o campo permaneça vermelho?

  • Convida um usuário a executar uma ação em um formulário que não seja um erro. Não há permissões suficientes para acessar um arquivo? Por que não pedir ao usuário para conceder permissões administrativas ou escolher um arquivo diferente?

  • Se nada mais funcionar, mostra um erro útil e amigável, escrito especificamente para reduzir a frustração do usuário, ajudar o usuário a entender o que deu errado e, eventualmente, resolver o problema, além de ajudá-lo a evitar o erro no futuro (quando aplicável).

    Na sua pergunta, você sugeriu duas mensagens em uma exceção: uma técnica para desenvolvedores e outra para usuários finais. Embora essa seja uma sugestão válida em alguns casos menores, a maioria das exceções é produzida em um nível em que é impossível produzir uma mensagem significativa para os usuários. Pegue DivisionByZeroExceptione imagine que não poderíamos impedir a exceção de acontecer e não podemos lidar com isso sozinhos. Quando a divisão ocorre, a estrutura (já que é a estrutura, e não o código comercial, que gera a exceção) sabe qual será uma mensagem útil para um usuário? Absolutamente não:

    A divisão por zero ocorreu. [ESTÁ BEM]

    Em vez disso, pode-se deixar lançar a exceção e depois capturá-la em um nível superior, onde conhecíamos o contexto comercial e poderíamos agir de acordo para realmente ajudar o usuário final, mostrando assim algo como:

    O campo D13 não pode ter o valor idêntico ao do campo E6, porque a subtração desses valores é usada como um divisor. [ESTÁ BEM]

    ou talvez:

    Os valores relatados pelo serviço ATP são inconsistentes com os dados locais. Isso pode ser causado pela falta de sincronização dos dados locais. Deseja sincronizar as informações de remessa e tentar novamente? [Sim não]

Essas mensagens não são para análise

As mensagens de exceção também não devem ser analisadas ou usadas programaticamente. Se você acha que informações adicionais podem ser necessárias pelo responsável pela chamada, inclua-as na exceção lado a lado com a mensagem. Isso é importante, porque a mensagem está sujeita a alterações sem aviso prévio. O tipo faz parte da interface, mas a mensagem não é: nunca dependa dela para tratamento de exceções.

Imagine a mensagem de exceção:

O tempo limite da conexão com o servidor de cache atingiu o tempo limite depois de esperar 500 ms. Aumente o tempo limite ou verifique o monitoramento de desempenho para identificar uma queda no desempenho do servidor. O tempo médio de espera para o servidor de armazenamento em cache foi de 6 ms. no último mês, 4 ms. para a última semana e 377 ms. pela última hora.

Você deseja extrair os valores "500", "6", "4" e "377". Pense um pouco na abordagem que você usará para fazer a análise e só então continue lendo.

Você tem a ideia? Ótimo.

Agora, o desenvolvedor original descobriu um erro de digitação:

Connecting to the caching sever timed out after waiting [...]

deveria estar:

                            ↓
Connecting to the caching server timed out after waiting [...]

Além disso, o desenvolvedor considera que o mês / semana / uma hora não são particularmente relevantes, portanto, ele também faz uma alteração adicional:

O tempo médio de espera para o servidor de armazenamento em cache foi de 6 ms. no último mês, 5 ms. nas últimas 24 horas e 377 ms. pela última hora.

O que acontece com a sua análise?

Em vez de analisar, você pode usar propriedades de exceção (que podem conter o que se deseja, assim que os dados podem ser serializados):

{
    message: "Connecting to the caching [...]",
    properties: {
        "timeout": 500,
        "statistics": [
            { "timespan": 1, "unit": "month", "average-timeout": 6 },
            { "timespan": 7, "unit": "day", "average-timeout": 4 },
            { "timespan": 1, "unit": "hour", "average-timeout": 377 },
        ]
    }
}

Quão fácil é usar esses dados agora?

Às vezes (como no .NET), a mensagem pode até ser traduzida para o idioma do usuário (IMHO, a tradução dessas mensagens é absolutamente errada, pois é esperado que qualquer desenvolvedor possa ler em inglês). A análise dessas mensagens é quase impossível.


2
Bom ponto sobre o usuário final. Eu acrescentaria que o usuário final também não deve ver mensagens de exceção por razões de segurança. O conhecimento da natureza exata de um erro para um usuário não-leigo pode apresentar uma exploração. O usuário final deve saber apenas o erro no contexto do que estava fazendo.
Neil

1
@ Neil: isso é um pouco teórico. Na prática, os benefícios de ocultar os logs do usuário são superados pela complexidade do log on-line. Sem contar o aspecto amigável. Imagine que você está instalando o Visual Studio em sua nova VM do Windows. De repente, a instalação falha. Você prefere ir aos logs e diagnosticar o problema sozinho (com a ajuda do Stack Exchange e blogs) ou esperar até que a Microsoft resolva o problema e publique um hotfix?
Arseni Mourzenko

4
Acho que todo mundo está ciente de que a mensagem "referência de objeto ..." é inútil. A questão relevante, porém, é qual código de máquina você gostaria que o jitter gerasse que produza a mensagem que você deseja? Se você fizer esse exercício, descobrirá rapidamente que a mensagem não pode ser gerada facilmente sem um enorme custo de desempenho imposto a todos os casos não excepcionais . O benefício de facilitar um pouco o trabalho de um desenvolvedor descuidado não é pago pelo desempenho atingido pelo usuário final.
precisa

7
Em relação às exceções de segurança: quanto menos informações na exceção, melhor. Minha anedota favorita é que, em uma versão pré-beta do .NET 1.0, você pode receber uma mensagem de exceção como "Você não tem permissão para determinar o nome do arquivo C: \ foo.txt". Ótimo, obrigado por essa mensagem de exceção detalhada!
precisa

2
Discordo de nunca mostrar ao usuário final uma mensagem de erro detalhada. Ocorreu-me muitas vezes que recebi uma mensagem de erro enigmática que me impedia de iniciar meu jogo / software, mas, quando o pesquiso no Google, descobri que há uma solução fácil. Sem essa mensagem de erro, não havia como encontrar a solução alternativa. Talvez seja melhor ocultar os detalhes em um botão "mais detalhes"?
BlueRaja - Danny Pflughoeft

2

A resposta para isso depende inteiramente da exceção.

A pergunta mais importante a fazer é "quem pode resolver o problema?" Se a exceção for causada por um erro do sistema de arquivos enquanto você abre um arquivo, pode fazer sentido transmitir essa mensagem ao usuário final. A mensagem pode conter mais informações sobre como corrigir o erro. Eu posso pensar em um caso definitivo no meu trabalho em que eu tinha um sistema de plug-ins que carregava DLLs. Se um usuário digitou um erro de digitação na linha de comando para carregar o plug-in errado, a mensagem de erro subjacente do sistema realmente continha informações úteis para permitir a correção do problema.

Na maioria das vezes, no entanto, uma exceção não capturada não pode ser corrigida pelo usuário. A maioria deles envolve fazer alterações no código. Nesses casos, o consumidor da mensagem é claramente um desenvolvedor.

O caso de exceções detectadas é mais complicado porque você tem a oportunidade de processar adequadamente a exceção e lançar uma mais amigável. Uma linguagem de script que escrevi lançou exceções cuja mensagem era claramente destinada a um desenvolvedor. No entanto, como a pilha se desenrolou, adicionei traços de pilha legíveis pelo usuário de dentro da linguagem de script. Meus usuários raramente conseguiam entender a mensagem, mas podiam observar o rastreamento da pilha no script e descobrir o que acontecia 95% do tempo. Para os 5% restantes, quando eles me ligaram, tínhamos não apenas um rastreamento de pilha, mas uma mensagem pronta para desenvolvedor para me ajudar a descobrir o que estava errado.

No total, trato a mensagem de exceção como um conteúdo desconhecido. Alguém mais acima, que sabia mais sobre as implementações, deu ao meu software uma exceção. Às vezes, tenho um palpite de que conteúdo desconhecido será útil para um usuário final, às vezes não.


1
"Se a exceção é causada por um erro no sistema de arquivos enquanto você abre um arquivo, pode fazer sentido transmitir essa mensagem ao usuário final": mas quando você lança o erro no sistema de arquivos, não conhece o contexto: pode ser a ação do usuário ou algo completamente não relacionado (como seu aplicativo fazendo um backup de alguma configuração, sem que o usuário perceba). Isso implica que você terá um bloco try / catch que mostra uma mensagem de erro , muito diferente de mostrar a mensagem de exceção diretamente.
Arseni Mourzenko

@ MainMa Você pode não conhecer o contexto explicitamente, mas há várias dicas. Por exemplo, se eles estão abrindo um arquivo e a exceção é "IOError: permissão negada", há uma chance razoável de o usuário juntar 2 e 2 e descobrir o arquivo em questão. Meu editor favorito faz isso - ele apenas envia o texto da exceção em uma caixa de diálogo, sem penugem. Por outro lado, se eu estava carregando meu aplicativo e obtive "permissão negada" ao carregar um monte de imagens, é muito menos razoável supor que o usuário possa fazer algo com as informações.
Cort Ammon - Restabelece Monica

Argumentando o seu argumento, nunca vi nenhum valor em mostrar a um usuário uma NullReferenceException, exceto que o mero ato de exibir essa mensagem mostra que seu software não tinha idéia de como se recuperar! Essa mensagem de exceção nunca é útil para um usuário final.
Cort Ammon - Restabelece Monica
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.