Qual é a maneira correta de exibir a InnerException completa?

155

Qual é a maneira correta de mostrar a minha plenitude InnerException.

Descobri que algumas das minhas InnerExceptions têm outra InnerExceptione isso é bastante profundo.

Farei InnerException.ToString()o trabalho para mim ou preciso fazer um loop InnerExceptionse criar um Stringcom StringBuilder?

Willem
fonte
Por que você precisa mostrar a exceção interna?
Akram Shahda
26
@ Akram porque na maioria das vezes é a exceção interna que é interessante. Um exemplo é o XmlSerializer, que lança uma InvalidOperationException sempre que algo der errado. O que deu errado está na exceção interna.
Adrianm
4
@AkramShahda Bem, talvez você queira usar esse método no seu log?
cederlof
Mensagem relacionada - Exception.Message vs Exception.ToString ()
RBT

Respostas:

239

Você pode simplesmente imprimir exception.ToString()- isso também incluirá o texto completo para todos os InnerExceptions aninhados .

Jon
fonte
18
Isto inclui uma carga de outra porcaria também, não apenas a mensagem de exceção e mensagens de exceção interna
ᴍᴀᴛᴛ ʙᴀᴋᴇʀ
apenas por uma questão de sucessão, você não precisa realmente do .ToString (), apenas usar a exceção fará o mesmo.
Alex Stephens
3
@AlexStephens você está certo, mas somente se você tem uma conversão implícita "a string" por algum motivo, como precedente string: "bla" + exceção
oo_dev
1
FYI: ele não chamará ToStringmétodos personalizados para exceções internas, conforme detalhado em Por que System.Exception.ToString não chama ToString virtual para exceções internas? .
Jeff B
45

Apenas use exception.ToString()

http://msdn.microsoft.com/en-us/library/system.exception.tostring.aspx

A implementação padrão do ToString obtém o nome da classe que lançou a exceção atual, a mensagem, o resultado da chamada do ToString na exceção interna e o resultado da chamada do Environment.StackTrace. Se algum desses membros for nulo, seu valor não será incluído na string retornada.

Se não houver mensagem de erro ou se for uma sequência vazia (""), nenhuma mensagem de erro será retornada. O nome da exceção interna e o rastreamento da pilha serão retornados apenas se não forem nulos.

exception.ToString () também chamará .ToString () na exceção interna dessa exceção e assim por diante ...

Rob P.
fonte
45

Eu costumo fazer assim para remover a maior parte do ruído:

void LogException(Exception error) {
    Exception realerror = error;
    while (realerror.InnerException != null)
        realerror = realerror.InnerException;

    Console.WriteLine(realerror.ToString())
}    

Edit: Eu esqueci esta resposta e está surpreso que ninguém apontou que você pode simplesmente fazer

void LogException(Exception error) {
    Console.WriteLine(error.GetBaseException().ToString())
}    
adrianm
fonte
Este método oculta tudo, exceto a exceção interna mais profunda. Se isso fosse algo mundano como um erro "Divida por zero", não seria claro onde ocorreu e o que levou a isso. Obviamente, um rastreamento de pilha cheia geralmente é um exagero confuso, mas apenas a leitura da exceção interna é o outro extremo. A resposta do usuário3016982 é muito melhor. Você recebe todas as mensagens de exceção na pilha sem o rastreamento desagradável.
JamesHoux
1
@JamesHoux Qual é a resposta "user3016982"? Não consigo encontrá-lo aqui.
Maracuja-juice #
O usuário3016982 é ThomazMoura, consulte: stackoverflow.com/users/3016982/thomazmoura
Apfelkuacha
@JamesHoux, a exceção interna possui um rastreamento de pilha completo, mostrando onde ocorreu o erro e o que levou a ele. Não entenda quais informações extras você obtém dos rastreamentos de pilha removidos. Mensagens de exceção são outra coisa e pode ser útil coletar todas elas.
adrianm
2
Por que você não usa error.GetBaseException()? Eu acredito que este faz o mesmo ...
Robba
37

@ A resposta de Jon é a melhor solução quando você deseja detalhes completos (todas as mensagens e o rastreamento da pilha) e a recomendada.

No entanto, pode haver casos em que você apenas deseja as mensagens internas e, nesses casos, eu uso o seguinte método de extensão:

public static class ExceptionExtensions
{
    public static string GetFullMessage(this Exception ex)
    {
        return ex.InnerException == null 
             ? ex.Message 
             : ex.Message + " --> " + ex.InnerException.GetFullMessage();
    }
}

Costumo usar esse método quando tenho ouvintes diferentes para rastrear e registrar e quero ter visões diferentes sobre eles. Dessa forma, posso ter um ouvinte que envia todo o erro com rastreamento de pilha por email para a equipe de desenvolvimento para depuração usando o .ToString()método e um que grava um arquivo de logon com o histórico de todos os erros que ocorreram todos os dias sem o rastreamento de pilha com o .GetFullMessage()método

ThomazMoura
fonte
7
FYI Se exé um AggregateException, nenhuma das exceções internas serão incluídas nesta saída
kornman00
3
Esse deve ser um método .NET padrão. Todo mundo deveria estar usando isso.
JamesHoux
9

Para imprimir apenas a Messageparte s de exceções profundas, você pode fazer algo assim:

public static string ToFormattedString(this Exception exception)
{
    IEnumerable<string> messages = exception
        .GetAllExceptions()
        .Where(e => !String.IsNullOrWhiteSpace(e.Message))
        .Select(e => e.Message.Trim());
    string flattened = String.Join(Environment.NewLine, messages); // <-- the separator here
    return flattened;
}

public static IEnumerable<Exception> GetAllExceptions(this Exception exception)
{
    yield return exception;

    if (exception is AggregateException aggrEx)
    {
        foreach (Exception innerEx in aggrEx.InnerExceptions.SelectMany(e => e.GetAllExceptions()))
        {
            yield return innerEx;
        }
    }
    else if (exception.InnerException != null)
    {
        foreach (Exception innerEx in exception.InnerException.GetAllExceptions())
        {
            yield return innerEx;
        }
    }
}

Isso recursivamente passa por todas as exceções internas (incluindo o caso de AggregateExceptions) para imprimir todas as Messagepropriedades contidas nelas, delimitadas por quebra de linha.

Por exemplo

var outerAggrEx = new AggregateException(
    "Outer aggr ex occurred.",
    new AggregateException("Inner aggr ex.", new FormatException("Number isn't in correct format.")),
    new IOException("Unauthorized file access.", new SecurityException("Not administrator.")));
Console.WriteLine(outerAggrEx.ToFormattedString());

Ocorreu um aumento externo.
Agregado interno ex.
O número não está no formato correto.
Acesso a arquivos não autorizado.
Não é administrador.


Você precisará ouvir outras propriedades de exceção para obter mais detalhes. Por exemplo Data, terá algumas informações. Você poderia fazer:

foreach (DictionaryEntry kvp in exception.Data)

Para obter todas as propriedades derivadas (não na Exceptionclasse base ), você pode:

exception
    .GetType()
    .GetProperties()
    .Where(p => p.CanRead)
    .Where(p => p.GetMethod.GetBaseDefinition().DeclaringType != typeof(Exception));
nawfal
fonte
+1, é quase exatamente a mesma coisa que eu. Considere procurar uma propriedade implementada em IEnumerable<Exception>vez de codificada AggregrateExceptionpara lidar com outros tipos semelhantes. Exclua também p.IsSpecialNamee pi.GetIndexParameters().Length != 0evite problemas. Incluindo o nome do tipo de exceção na saída também é uma boa idéia
adrianm
@adrianm bom argumento sobre verificações de informações de propriedades. Em relação à verificação de coleta de exceções, é tudo sobre onde você deseja desenhar a linha. Claro que pode ser feito também ..
Nawfal
4

Eu faço:

namespace System {
  public static class ExtensionMethods {
    public static string FullMessage(this Exception ex) {
      if (ex is AggregateException aex) return aex.InnerExceptions.Aggregate("[ ", (total, next) => $"{total}[{next.FullMessage()}] ") + "]";
      var msg = ex.Message.Replace(", see inner exception.", "").Trim();
      var innerMsg = ex.InnerException?.FullMessage();
      if (innerMsg is object && innerMsg!=msg) msg = $"{msg} [ {innerMsg} ]";
      return msg;
    }
  }
}

Isso "imprime" todas as exceções internas e também lida com AggregateExceptions e casos em que InnerException.Message é o mesmo que Message

kofifus
fonte
3

Se você quiser informações sobre todas as exceções, use exception.ToString(). Ele coletará dados de todas as exceções internas.

Se você deseja apenas a exceção original, use exception.GetBaseException().ToString(). Isso fornecerá a primeira exceção, por exemplo, a exceção interna mais profunda ou a exceção atual, se não houver nenhuma exceção interna.

Exemplo:

try {
    Exception ex1 = new Exception( "Original" );
    Exception ex2 = new Exception( "Second", ex1 );
    Exception ex3 = new Exception( "Third", ex2 );
    throw ex3;
} catch( Exception ex ) {
    // ex => ex3
    Exception baseEx = ex.GetBaseException(); // => ex1
}
dkostas
fonte
2

acúmulo na resposta de nawfal.

ao usar sua resposta, faltava uma variável aggrEx, adicionei.

arquivo ExceptionExtenstions.class:

// example usage:
// try{ ... } catch(Exception e) { MessageBox.Show(e.ToFormattedString()); }

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace YourNamespace
{
    public static class ExceptionExtensions
    {

        public static IEnumerable<Exception> GetAllExceptions(this Exception exception)
        {
            yield return exception;

            if (exception is AggregateException )
            {
                var aggrEx = exception as AggregateException;
                foreach (Exception innerEx in aggrEx.InnerExceptions.SelectMany(e => e.GetAllExceptions()))
                {
                    yield return innerEx;
                }
            }
            else if (exception.InnerException != null)
            {
                foreach (Exception innerEx in exception.InnerException.GetAllExceptions())
                {
                    yield return innerEx;
                }
            }
        }


        public static string ToFormattedString(this Exception exception)
        {
            IEnumerable<string> messages = exception
                .GetAllExceptions()
                .Where(e => !String.IsNullOrWhiteSpace(e.Message))
                .Select(exceptionPart => exceptionPart.Message.Trim() + "\r\n" + (exceptionPart.StackTrace!=null? exceptionPart.StackTrace.Trim():"") );
            string flattened = String.Join("\r\n\r\n", messages); // <-- the separator here
            return flattened;
        }
    }
}
Shimon Doodkin
fonte
Eu tive uma exceção porque:e.StackTrace == null
Andrei Krasutski 6/06/19
1
Eu atualizei .Select (e => e.Message.Trim () + "\ r \ n" + (e.StackTrace! = Null? StackTrace.Trim (): "")); talvez isso ajude
Shimon Doodkin