Ведение журнала и трассировка в приложениях .NET

Завершено

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

Трассировка — это способ отслеживать выполнение приложения во время его работы. Вы можете добавить инструментирование трассировки и отладки в приложение .NET при его разработке. Это инструментирование можно использовать во время разработки приложения и после его развертывания.

Этот простой прием является удивительно эффективным. Его можно использовать, когда одного отладчика недостаточно, как описано ниже.

  • Проблемы, возникающие в течение длительных периодов времени, может быть трудно отлаживать с помощью традиционного отладчика. Журналы позволяют детализировать проверку после смерти, которая охватывает длительные периоды времени. В отладчиках, напротив, доступен лишь анализ в режиме реального времени.
  • Многопоточные приложения и распределенные приложения часто трудно отлаживать. Присоединение отладчика часто изменяет поведение приложения. Подробные журналы можно анализировать по мере необходимости, чтобы понять сложные системы.
  • Проблемы в распределенных приложениях могут возникать из-за сложного взаимодействия между многими компонентами. Нецелесообразно подключать отладчик к каждой части системы.
  • Многие службы нельзя останавливать. Присоединение отладчика часто приводит к превышению времени ожидания.
  • Проблемы не всегда удается прогнозировать. Ведение журнала и трассировка создают очень низкую нагрузку, поэтому программы будут сохранять данные даже при возникновении проблемы.

Запись данных в окна вывода

До этого момента мы использовали только консоль для вывода сведений пользователю приложения. На основе .NET можно создать и другие приложения с пользовательскими интерфейсами, например мобильные приложения, веб-приложения и классические приложения, в которых нет видимой консоли. В таких приложениях сообщения выводятся с помощью System.Console в фоновом режиме. Эти сообщения могут отображаться в окне вывода Visual Studio или Visual Studio Code. Их также можно направить в системный журнал, например logcat в Android. В результате при использовании System.Console.WriteLine в приложении, не являющемся консольным, следует тщательно все обдумать.

Здесь можно использовать System.Diagnostics.Debug и System.Diagnostics.Trace в дополнение к System.Console. Debug и Trace являются частью System.Diagnostics и будут записывать данные в журнал только при присоединении соответствующего прослушивателя.

Вы можете выбрать любой из этих API стиля печати. Ниже описаны основные различия.

  • System.Console
    • Всегда включен и всегда записывает данные в консоль.
    • Полезно для сведений, которые могут потребоваться клиенту в выпуске.
    • Это самый простой подход. Он часто используется для краткосрочных и непредвиденных действий по отладке. Такой код отладки часто даже не попадает в систему управления версиями.
  • System.Diagnostics.Trace
    • Включается только при наличии определения TRACE.
    • Записывает данные в подключенные прослушиватели, по умолчанию это DefaultTraceListener.
    • Используйте этот API при создании журналов, которые будут включены в большинстве сборок.
  • System.Diagnostics.Debug
    • Включается только при определении DEBUG (в режиме отладки).
    • Передает данные в присоединенный отладчик.
    • Используйте этот API при создании журналов, которые будут включены только в сборках отладки.
Console.WriteLine("This message is readable by the end user.")
Trace.WriteLine("This is a trace message when tracing the app.");
Debug.WriteLine("This is a debug message just for developers.");

При проектировании стратегии трассировки и отладки подумайте о том, как должны выглядеть выходные данные. При наличии нескольких операторов Write, заполненных несвязанными данными, получается сложный для чтения журнал. С другой стороны, при использовании метода WriteLine для размещения связанных операторов на разных строках может быть сложно понять, какие сведения связаны друг с другом. Как правило, при использовании нескольких операторов Write имеет смысл объединить информацию из разных источников для создания единого информационного сообщения. Если необходимо создать одно полное сообщение, используйте инструкцию WriteLine.

Debug.Write("Debug - ");
Debug.WriteLine("This is a full line.");
Debug.WriteLine("This is another full line.");

Эти выходные данные выводятся из предыдущего журнала с Debug:

Debug - This is a full line.
This is another full line.

Определение констант TRACE и DEBUG

По умолчанию при запуске приложения в режиме отладки определяется константа DEBUG. Вы можете управлять этим, добавив DefineConstants запись в файл проекта в группу свойств. Ниже приведен пример включения TRACE для конфигураций Debug и Release наряду с DEBUG для конфигураций Debug.

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
    <DefineConstants>DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
    <DefineConstants>TRACE</DefineConstants>
</PropertyGroup>

Если при использовании Trace нет подключения к отладчику, необходимо настроить прослушиватель трассировки, например dotnet-trace.

Условная трассировка

Помимо простых методов Write и WriteLine, можно использовать WriteIf и WriteLineIf с дополнительными условиями. Например, следующая логика проверяет, равен ли счетчик нулю, а затем записывает отладочное сообщение:

if(count == 0)
{
    Debug.WriteLine("The count is 0 and this may cause an exception.");
}

Его можно переписать в одной строке кода:

Debug.WriteLineIf(count == 0, "The count is 0 and this may cause an exception.");

Эти условия также можно использовать с Trace флагами, которые определяются в приложении:

bool errorFlag = false;  
System.Diagnostics.Trace.WriteIf(errorFlag, "Error in AppendData procedure.");  
System.Diagnostics.Debug.WriteIf(errorFlag, "Transaction abandoned.");  
System.Diagnostics.Trace.Write("Invalid value for data request");

Проверка существования определенных условий

Оператор проверочного утверждения Assert проверяет выполнение условия, указанного в качестве аргумента для оператора Assert. Если условие выполняется, никаких действий не производится. Если же условие не выполняется, то утверждение выдает ошибку. Если вы работаете с отладочной сборкой, программа переходит в режим прерывания.

Метод Assert можно использовать из Debug или Trace, которые находятся в пространстве имен System.Diagnostics. Методы класса Debug не включаются в окончательную версию (версию выпуска) программы, поэтому они не увеличивают ее размер и не уменьшают скорость ее выполнения.

Метод System.Diagnostics.Debug.Assert можно свободно использовать для проверки условий, которые должны выполняться, если код программы написан правильно. Предположим, вы написали функцию деления целых чисел. По правилам математики делитель не может быть равен нулю. Вы можете проверить это условие с помощью утверждения:

int IntegerDivide(int dividend, int divisor)
{
    Debug.Assert(divisor != 0, $"{nameof(divisor)} is 0 and will cause an exception.");

    return dividend / divisor;
}

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

Примечание.

При использовании System.Diagnostics.Debug.Assert следите за тем, чтобы код в операторе Assert не изменял результаты программы в случае его удаления. В противном случае вы можете случайно допустить ошибку, которая проявится только в версии выпуска программы. Будьте особенно осторожны с операторами утверждения, которые содержат вызовы функций или процедур.

Как видите, использование Debug и Trace из пространства имен System.Diagnostics отлично подходит для предоставления дополнительного контекста при выполнении и отладке приложения.