В .net Исключение, как получить stacktrace с значениями аргумента
Я пытаюсь добавить обработчик необработанных исключений в .net(С#), который должен быть как можно более полезным для "пользователя". Конечные пользователи в основном программисты, поэтому им просто нужно намекнуть, какой объект они манипулируют неправильно.
Я разрабатываю окна, похожие на отчет об ошибках Windows XP при сбое приложения, но это дает как можно более достоверную информацию об исключении.
В то время как трассировка стека позволяет мне (поскольку у меня есть исходный код), чтобы определить источник проблемы, пользователи не имеют ее, и поэтому они теряются без дополнительной информации. Излишне говорить, что я должен тратить много времени на поддержку инструмента.
Есть несколько системных исключений, таких как KeyNotFoundException, которые вызывают коллекция Dictionary, которые действительно меня пугают, поскольку они не включают в сообщение ключ, который не был найден. Я могу заполнить свой код тоннами блоков catch catch, но он довольно агрессивен и содержит намного больше кода для поддержки, не говоря уже о тонне больше строк, которые должны быть локализованы.
Наконец, вопрос: есть ли способ получить (во время выполнения) значения аргументов каждой функции в трассировке стека вызовов?
Только это может решить 90% запросов на поддержку.
Ответы
Ответ 1
Я не думаю, что System.Diagnostics.StackFrame предоставляет информацию о аргументах (кроме сигнатуры метода).
Вы можете выявить неприятные вызовы с протоколом трассировки через AOP или даже использовать функции перехвата исключений для условного журнала, не забирая ваш код. Посмотрите http://www.postsharp.org/.
Ответ 2
Аналогично, я не нашел ничего, чтобы автоматически выводить параметры во время выполнения. Вместо этого я использовал надстройку Visual Studio для генерации кода, который явно упаковывает параметры, например:
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;
}
}
}
}
EDIT: или, альтернативно, сделав параметр HandleException массивом 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;
}
...
Немного боль, генерирующая дополнительный код, чтобы явно передать параметры обработчику исключений, но с помощью надстройки вы можете как минимум автоматизировать его.
Пользовательский атрибут может использоваться для аннотирования любых параметров, которые вы не хотите, чтобы надстройка передавала обработчик исключений:
public UserToken RegisterUser( string userId, [NoLog] string password )
{
}
2ND EDIT:
Помните, я полностью забыл об AVICode:
http://www.avicode.com/
Они используют методы перехвата вызовов для обеспечения именно такого рода информации, поэтому это должно быть возможно.
Ответ 3
К сожалению, вы не можете получить фактические значения параметров из callstack, за исключением инструментов отладки, фактически прикрепленных к приложению. Однако, используя объекты StackTrace и StackFrame в System.Diagnostics, вы можете ходить в стек вызовов и считывать все вызванные методы, а также имена и типы параметров. Вы сделали бы это так:
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());
}
Если вам нужны фактические значения, вам нужно будет принимать мини-накопители и анализировать их, я боюсь. Информацию о получении информации о дампах можно найти по адресу:
http://www.debuginfo.com/tools/clrdump.html
Ответ 4
Существует программный инструмент от redgate, который выглядит очень перспективным.
http://www.red-gate.com/products/dotnet-development/smartassembly/
В настоящее время мы собираем отчеты об ошибках наших клиентов по электронной почте, и я иногда борюсь с некоторыми важными данными, отсутствующими (в основном некоторые очень простые переменные, такие как идентификатор из текущей записи, поэтому я могу воспроизвести ошибку). Я еще не тестировал этот инструмент, но из своего понимания он выполняет то, что собирает значения аргументов и локальные переменные из стека.
Ответ 5
Я не верю, что есть встроенный механизм. Извлечение каждого кадра трассировки стека, позволяя вам определять метод в трассировке, дает только информацию о отраженном типе этого метода. Нет информации о параметрах. Я думаю, именно поэтому некоторые исключения, в частности, ArgumentException, et. и др. предоставить механизм для указания значения аргумента, участвующего в исключении, поскольку нет простого способа его получить.
Ответ 6
Поскольку конечные пользователи являются разработчиками, вы можете предоставить им версию, которая позволяет регистрировать все передаваемые ключевые значения/аргументы. И предоставить средство, чтобы они могли включать/выключать ведение журнала.
Ответ 7
теоретически можно делать то, что вы хотите, используя формат файла Portable Executable (PE), чтобы получить типы переменных и смещения, но я столкнулся с [документацией] стены, пытаясь сделать это пару лет назад, Удачи!
Ответ 8
Если бы вы могли делать то, что искали, вы бы победили неотъемлемую часть безопасности .NET.
Наилучший вариант в этом случае - привязка к отладчику или профилировщику (любой из них может получить доступ к этим значениям). Первый может быть присоединен, последний должен быть активным до запуска программы.
Ответ 9
Я ничего не знаю об этом, но хотел сам. Обычно я делаю это сам, бросая исключения, но если это не ваш бросок, тогда вы можете быть вместе с остальными из нас.
Ответ 10
Я не знаю способ получить значения аргументов каждой функции в трассировке стека вызовов, но одно решение - поймать конкретное исключение (KeyNotFoundException
в вашем примере) и перебросить его в новое исключение. Это позволяет вам связать любую дополнительную информацию, которую вы пожелаете
Например:
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
Я ценю ваше выражение о локализации строк ошибок, но если ваша аудитория "в основном программисты", то соглашение уже диктует понимание английского языка в некоторой степени (правильно или неправильно, но это еще одно обсуждение!)
Ответ 11
Как только у вас есть исключение, две вещи, которые вас интересуют, - System.Diagnostics.StackTrace и System.Diagnostics.StackFrame
Существует пример MSDN здесь