В исключении .net как получить трассировку стека со значениями аргументов - PullRequest
38 голосов
/ 01 октября 2008

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

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

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

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

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

Ответы [ 11 ]

7 голосов
/ 01 октября 2008

Я не думаю, что System.Diagnostics.StackFrame предоставляет информацию об аргументах (кроме сигнатуры метода).

Вы могли бы обрабатывать проблемные вызовы с помощью журналирования трассировки через AOP или даже использовать его функции перехвата исключений для условной регистрации без необходимости засорять ваш код. Осмотрите http://www.postsharp.org/.

6 голосов
/ 01 октября 2008

Аналогично, я не нашел ничего, что бы автоматически выводило параметры во время выполнения. Вместо этого я использовал надстройку 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;
            }
        }
    }
}

РЕДАКТИРОВАТЬ: или, альтернативно, путем присвоения параметру 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 )
{
}

2-е РЕДАКТИРОВАНИЕ:

Имейте в виду, я бы полностью забыл про AVICode:

http://www.avicode.com/

Они используют методы перехвата вызовов, чтобы предоставить именно такую ​​информацию, поэтому это должно быть возможно.

3 голосов
/ 01 октября 2008

К сожалению, вы не можете получить фактические значения параметров из стека вызовов, кроме как с помощью средств отладки, фактически подключенных к приложению. Однако с помощью объектов 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

2 голосов
/ 20 июня 2011

Существует программный инструмент от Redgate, который выглядит очень многообещающе.

http://www.red -gate.com / продукция / DotNet-разработка / smartassembly /

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

1 голос
/ 01 октября 2008

Если бы вы могли делать то, что искали, вы бы победили неотъемлемую часть безопасности .NET.

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

1 голос
/ 01 октября 2008

теоретически возможно сделать то, что вы хотите, используя преимущества формата файла Portable Executable (PE) для получения типов переменных и смещений, но я наткнулся на стену [документации], пытаясь сделать это пару лет назад , Удачи!

1 голос
/ 01 октября 2008

Поскольку конечные пользователи являются разработчиками, вы можете предоставить им версию, которая позволяет регистрировать все передаваемые ключевые значения / аргументы. И предоставьте средство, чтобы они могли включить / выключить ведение журнала.

1 голос
/ 01 октября 2008

Я не верю, что есть встроенный механизм. Получение каждого фрейма трассировки стека, позволяя вам определить метод в трассировке, дает только отраженную информацию о типе этого метода. Нет информации о параметрах. Я думаю, именно поэтому некоторые исключения, в частности, ArgumentException, et. и др. предоставить механизм для указания значения аргумента, участвующего в исключении, поскольку нет простого способа получить его.

0 голосов
/ 01 октября 2008

Как только у вас возникнет исключение, вас интересуют две вещи: System.Diagnostics.StackTrace и System.Diagnostics.StackFrame

Пример MSDN здесь

0 голосов
/ 01 октября 2008

Я не знаю, как получить значения аргументов каждой функции в трассировке стека вызовов, но одним из решений было бы перехватить конкретное исключение (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

Я ценю ваше заявление о локализации строк ошибок, но если ваша аудитория «в основном программисты», тогда соглашение уже в некоторой степени требует понимания английского языка (правильно или неправильно - но это еще одна дискуссия!)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...