¿Cuál es la forma correcta de mostrar la InnerException completa?

155

¿Cuál es la forma correcta de mostrar mi plenitud InnerException?

Descubrí que algunas de mis InnerExceptions tienen otra InnerExceptiony eso es bastante profundo.

¿Haré InnerException.ToString()el trabajo por mí o necesito recorrer InnerExceptionsy construir un Stringcon StringBuilder?

Willem
fuente
¿Por qué necesitas mostrar la excepción interna?
Akram Shahda
26
@ Akram porque la mayoría de las veces es la excepción interna lo que es interesante. Un ejemplo es el XmlSerializer que solo arroja una InvalidOperationException cada vez que algo sale mal. Lo que salió mal está en la excepción interna.
adrianm
44
@AkramShahda Bueno, ¿tal vez quieras usar este método en tu registro?
cederlof
Publicación relacionada: Exception.Message vs Exception.ToString ()
RBT

Respuestas:

239

Simplemente puede imprimir exception.ToString(), eso también incluirá el texto completo de todos los correos InnerExceptionelectrónicos anidados .

Jon
fuente
18
Esto incluye una carga de otra basura también, no solo el mensaje de excepción y los mensajes de excepción internos
ʙᴀᴋᴇʀ
solo para ser sucinto, en realidad no necesita .ToString (), solo usar la excepción hará lo mismo.
Alex Stephens
3
@AlexStephens tiene razón, pero solo si tiene una conversión implícita "a cadena" por alguna razón, como la cadena anterior: "bla" + excepción
oo_dev
1
FYI: no llamará a ToStringmétodos personalizados para excepciones internas como se detalla en ¿Por qué System.Exception.ToString no llama a ToString virtual para excepciones internas? .
Jeff B
45

Solo usa exception.ToString()

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

La implementación predeterminada de ToString obtiene el nombre de la clase que arrojó la excepción actual, el mensaje, el resultado de llamar a ToString en la excepción interna y el resultado de llamar a Environment.StackTrace. Si alguno de estos miembros es nulo, su valor no se incluye en la cadena devuelta.

Si no hay un mensaje de error o si se trata de una cadena vacía (""), no se devuelve ningún mensaje de error. El nombre de la excepción interna y el seguimiento de la pila se devuelven solo si no son nulos.

exception.ToString () también llamará a .ToString () en la excepción interna de esa excepción, y así sucesivamente ...

Rob P.
fuente
45

Por lo general, me gusta esto para eliminar la mayor parte del ruido:

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

    Console.WriteLine(realerror.ToString())
}    

Editar: Olvidé esta respuesta y me sorprende que nadie haya señalado que solo puedes hacer

void LogException(Exception error) {
    Console.WriteLine(error.GetBaseException().ToString())
}    
adrianm
fuente
Este método oculta todo excepto la más profunda excepción interna. Si eso fuera algo mundano como un error de "Dividir por cero", no estaría claro dónde ocurrió y qué condujo a ello. Obviamente, un seguimiento completo de la pila suele ser una exageración desordenada, pero solo leer la excepción interna es el otro extremo. La respuesta de user3016982 es mucho mejor. Obtiene cada mensaje de excepción en la pila sin el rastro desagradable.
JamesHoux
1
@JamesHoux ¿Cuál es la respuesta "user3016982"? No puedo encontrarlo aquí.
maracuja-juice
El usuario 3016982 es ThomazMoura, consulte: stackoverflow.com/users/3016982/thomazmoura
Apfelkuacha
@JamesHoux, la excepción interna tiene un seguimiento completo de la pila que muestra dónde ocurrió el error y qué lo llevó a ello. No entiendo qué información adicional obtiene de los rastros de pila eliminados. Los mensajes de excepción son otra cosa y puede ser útil recopilarlos todos.
adrianm
2
¿Por qué no lo usas error.GetBaseException()? Creo que esto hace lo mismo ...
Robba
37

La respuesta de @ Jon es la mejor solución cuando desea detalles completos (todos los mensajes y el seguimiento de la pila) y la recomendada.

Sin embargo, puede haber casos en los que solo desee los mensajes internos, y para estos casos uso el siguiente método de extensión:

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

A menudo uso este método cuando tengo diferentes oyentes para rastrear y registrar y quiero tener diferentes puntos de vista sobre ellos. De esa manera, puedo tener un oyente que envía el error completo con el seguimiento de la pila por correo electrónico al equipo de desarrollo para la depuración utilizando el .ToString()método y uno que escribe un archivo de inicio de sesión con el historial de todos los errores que ocurrieron cada día sin el seguimiento de la pila con El .GetFullMessage()método

ThomazMoura
fuente
77
FYI Si exes un AggregateException, ninguna de las excepciones internas se incluirá en esta salida
kornman00
3
Este debería ser un método .NET estándar. Todos deberían estar usando esto.
JamesHoux
9

Para imprimir solo la Messageparte de excepciones profundas, puede hacer algo como esto:

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;
        }
    }
}

Esto atraviesa recursivamente todas las excepciones internas (incluido el caso de AggregateExceptions) para imprimir todas las Messagepropiedades contenidas en ellas, delimitadas por el salto de línea.

P.ej

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());

Se produjo aggr externo.
Aggr interno ex.
El número no está en el formato correcto.
Acceso no autorizado a archivos.
No administrador


Necesitará escuchar otras propiedades de Excepción para más detalles. Por ejemplo Datatendrá alguna información. Podrías hacerlo:

foreach (DictionaryEntry kvp in exception.Data)

Para obtener todas las propiedades derivadas (no en la Exceptionclase base ), puede hacer:

exception
    .GetType()
    .GetProperties()
    .Where(p => p.CanRead)
    .Where(p => p.GetMethod.GetBaseDefinition().DeclaringType != typeof(Exception));
nawfal
fuente
+1, esto es casi exactamente lo mismo que yo. Considere buscar una implementación de propiedad en IEnumerable<Exception>lugar de una codificación rígida AggregrateExceptionpara manejar otros tipos similares. También excluir p.IsSpecialNamey pi.GetIndexParameters().Length != 0evitar problemas. Incluir el nombre del tipo de excepción en la salida también es una buena idea
adrianm el
@adrianm buen punto sobre verificaciones de información de propiedad. Con respecto a la verificación de la recopilación de excepciones, se trata de dónde desea trazar la línea. Claro que eso también se puede hacer ..
nawfal
4

Hago:

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;
    }
  }
}

Esto "imprime" todas las excepciones internas y también maneja AggregateExceptions y casos donde InnerException.Message es lo mismo que Message

kofifus
fuente
3

Si desea información sobre todas las excepciones, use exception.ToString() . Recopilará datos de todas las excepciones internas.

Si solo quieres la excepción original, úsala exception.GetBaseException().ToString(). Esto le dará la primera excepción, por ejemplo, la excepción interna más profunda o la excepción actual si no hay una excepción interna.

Ejemplo:

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
fuente
2

acumulación de la respuesta de nawfal.

cuando usé su respuesta faltaba una variable aggrEx, la agregué.

archivo 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
fuente
Tuve una excepción porque:e.StackTrace == null
Andrei Krasutski
1
He actualizado .Select (e => e.Message.Trim () + "\ r \ n" + (e.StackTrace! = Null? StackTrace.Trim (): "")); tal vez esto ayude
Shimon Doodkin