c# - que - En una excepción de.net cómo obtener una stacktrace con valores de argumento
ref c# (11)
Como los usuarios finales son desarrolladores, puede proporcionarles una versión que permita el registro de todos los valores / argumentos clave que se pasan. Y proporcione una instalación para que puedan activar / desactivar el registro.
Estoy intentando agregar un manejador de excepciones no manejado en .net (c #) que debería ser tan útil para el ''usuario'' como sea posible. Los usuarios finales son en su mayoría programadores, por lo que solo necesitan una pista de qué objeto están manipulando incorrectamente.
Estoy desarrollando una ventana similar al informe de error de Windows XP cuando una aplicación falla, pero eso da la mayor cantidad de información posible sobre la excepción lanzada.
Si bien el seguimiento de pila me permite (dado que tengo el código fuente) identificar el origen del problema, los usuarios no lo tienen y se pierden sin más información. Huelga decir que tengo que pasar mucho tiempo apoyando la herramienta.
Hay algunas excepciones del sistema como KeyNotFoundException lanzadas por la colección de diccionarios que realmente me molestan ya que no incluyen en el mensaje la clave que no se encontró. Puedo llenar mi código con toneladas de bloques de captura de prueba, pero es bastante agresivo y hay mucho más código para mantener, sin mencionar una tonelada más de cadenas que deben ser localizadas.
Finalmente, la pregunta: ¿hay alguna forma de obtener (en tiempo de ejecución) los valores de los argumentos de cada función en el seguimiento de la pila de llamadas? Solo eso podría resolver el 90% de las llamadas de soporte.
Del mismo modo, no he encontrado nada para derivar los parámetros automáticamente en tiempo de ejecución. En cambio, he usado un complemento de Visual Studio para generar código que empaqueta explícitamente los parámetros, como este:
public class ExceptionHandler
{
public static bool HandleException(Exception ex, IList<Param> parameters)
{
/*
* Log the exception
*
* Return true to rethrow the original exception,
* else false
*/
}
}
public class Param
{
public string Name { get; set; }
public object Value { get; set; }
}
public class MyClass
{
public void RenderSomeText(int lineNumber, string text, RenderingContext context)
{
try
{
/*
* Do some work
*/
throw new ApplicationException("Something bad happened");
}
catch (Exception ex)
{
if (ExceptionHandler.HandleException(
ex,
new List<Param>
{
new Param { Name = "lineNumber", Value=lineNumber },
new Param { Name = "text", Value=text },
new Param { Name = "context", Value=context}
}))
{
throw;
}
}
}
}
EDITAR: o alternativamente, al hacer que el parámetro para HandleException sea una matriz params:
public static bool HandleException(Exception ex, params Param[] parameters)
{
...
}
...
if (ExceptionHandler.HandleException(
ex,
new Param { Name = "lineNumber", Value=lineNumber },
new Param { Name = "text", Value=text },
new Param { Name = "context", Value=context}
))
{
throw;
}
...
Es un poco molesto generar el código adicional para pasar explícitamente los parámetros al manejador de excepciones, pero con el uso de un complemento al menos puedes automatizarlo.
Se puede usar un atributo personalizado para anotar cualquier parámetro que no desee que el complemento pase al manejador de excepciones:
public UserToken RegisterUser( string userId, [NoLog] string password )
{
}
2da EDICIÓN:
Eso sí, me había olvidado por completo de AVICode:
Usan técnicas de interceptación de llamadas para proporcionar exactamente este tipo de información, por lo que debe ser posible.
Hay una herramienta de software de redgate que parece muy prometedora.
http://www.red-gate.com/products/dotnet-development/smartassembly/
Actualmente, estamos recopilando los informes de error de nuestros clientes por correo electrónico y a veces me cuestan algunos datos importantes que faltan (sobre todo algunas variables muy básicas, como la identificación del registro actual para poder reproducir el error). Todavía no he probado esta herramienta, pero desde mi punto de vista hace lo que recoge los valores de los argumentos y las variables locales de la pila.
Lamentablemente, no puede obtener los valores reales de los parámetros de la pila de llamadas, excepto con las herramientas de depuración adjuntas a la aplicación. Sin embargo, al utilizar los objetos StackTrace y StackFrame en System.Diagnostics puede recorrer la pila de llamadas y leer todos los métodos invocados y los nombres y tipos de parámetros. Harías esto como:
System.Diagnostics.StackTrace callStack = new System.Diagnostics.StackTrace();
System.Diagnostics.StackFrame frame = null;
System.Reflection.MethodBase calledMethod = null;
System.Reflection.ParameterInfo [] passedParams = null;
for (int x = 0; x < callStack.FrameCount; x++)
{
frame = callStack.GetFrame(x);
calledMethod = frame.GetMethod();
passedParams = calledMethod.GetParameters();
foreach (System.Reflection.ParameterInfo param in passedParams)
System.Console.WriteLine(param.ToString());
}
Si necesita valores reales, entonces tendrá que tomar minivolcados y analizarlos, me temo. La información sobre cómo obtener información de volcado se puede encontrar en:
No creo que System.Diagnostics.StackFrame proporcione información sobre argumentos (que no sea la firma del método).
Puede instrumentar las llamadas problemáticas con el registro de seguimiento a través de AOP, o incluso utilizar sus funciones de interceptación de excepción para registrar condicionalmente sin tener que ensuciar su código. Echa un vistazo a http://www.postsharp.org/ .
No creo que haya un mecanismo incorporado. Al recuperar cada trama de la traza de pila, al mismo tiempo que le permite determinar el método en la traza, solo le proporciona la información del tipo reflejado en ese método. Sin información de parámetros. Creo que esta es la razón por la cual algunas excepciones, en particular, ArgumentException, et. Alabama. proporcionar un mecanismo para especificar el valor del argumento involucrado en la excepción ya que no hay una manera fácil de obtenerlo.
No estoy al tanto de nada de eso, pero lo he querido. Normalmente lo hago yo mismo cuando lanzo excepciones, pero si no es tuyo tirar, entonces puedes salir con el resto de nosotros.
No sé cómo obtener los valores de los argumentos de cada función en el seguimiento de la pila de llamadas, pero una solución sería capturar la excepción específica ( KeyNotFoundException
en su ejemplo) y volver a lanzarla en una nueva excepción. Eso le permite asociar cualquier información adicional que desee
Por ejemplo:
Dim sKey as String = "some-key"
Dim sValue as String = String.Empty
Try
sValue = Dictionary(sKey)
Catch KeyEx As KeyNotFoundException
Throw New KeyNotFoundException("Class.Function() - Couldn''t find [" & sKey & "]", KeyEx)
End Try
Agradezco su afirmación sobre la localización de las cadenas de error, pero si su audiencia es "en su mayoría programadores", entonces la convención ya dicta una comprensión del inglés hasta cierto punto (con razón o sin ella, ¡pero eso es otro debate!)
Si pudieras hacer lo que estás buscando, estarías derrotando una parte integral de la seguridad de .NET.
La mejor opción en el caso, es conectarlo a un depurador o perfilador (cualquiera de ellos puede acceder a esos valores). El primero se puede adjuntar, el último debe estar activo antes de que comience el programa.
Una vez que tenga la excepción, las dos cosas que le interesan son System.Diagnostics.StackTrace y System.Diagnostics.StackFrame
Aquí hay un ejemplo de MSDN
teóricamente es posible hacer lo que quiera aprovechando el formato de archivo de Portable Executable (PE) para obtener los tipos de variables y compensaciones, pero me encontré con un muro de [documentación] tratando de hacer esto hace un par de años. ¡Buena suerte!