Подтвердить что ты не робот

В .net Исключение, как получить stacktrace с значениями аргумента

Я пытаюсь добавить обработчик необработанных исключений в .net(С#), который должен быть как можно более полезным для "пользователя". Конечные пользователи в основном программисты, поэтому им просто нужно намекнуть, какой объект они манипулируют неправильно.

Я разрабатываю окна, похожие на отчет об ошибках Windows XP при сбое приложения, но это дает как можно более достоверную информацию об исключении.

В то время как трассировка стека позволяет мне (поскольку у меня есть исходный код), чтобы определить источник проблемы, пользователи не имеют ее, и поэтому они теряются без дополнительной информации. Излишне говорить, что я должен тратить много времени на поддержку инструмента.

Есть несколько системных исключений, таких как KeyNotFoundException, которые вызывают коллекция Dictionary, которые действительно меня пугают, поскольку они не включают в сообщение ключ, который не был найден. Я могу заполнить свой код тоннами блоков catch catch, но он довольно агрессивен и содержит намного больше кода для поддержки, не говоря уже о тонне больше строк, которые должны быть локализованы.

Наконец, вопрос: есть ли способ получить (во время выполнения) значения аргументов каждой функции в трассировке стека вызовов? Только это может решить 90% запросов на поддержку.

4b9b3361

Ответ 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 здесь