Диагностика в облаке

Управление протоколированием и трассировкой в Windows Azure

Майк Келли

Загрузить образец кода

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

А теперь быстро перескочим к тому времени, когда я занялся написанием кода, выполняемого на серверах. Я обнаружил, что эти выражения print теперь замысловато называются «протоколированием и трассировкой» и являются важной частью отладки для любого программиста серверного приложения.

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

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

В этой статье я объясню некоторые основные приемы протоколирования, трассировки и отладки, применяемые для серверных приложений. Затем мы подробно рассмотрим, как эти приемы можно задействовать в ваших проектах для Windows Azure. Попутно вы увидите, как используются протоколирование и трассировка в некоторых реальных приложениях, и я покажу, как с помощью Windows PowerShell управлять диагностикой выполняемого сервиса.

Стратегия протоколирования

В идеале, в любое серверное приложение — и обычно в любое веб-приложение, в том числе работающее под управлением Windows Azure, — изначально закладывается некая стратегия протоколирования и трассировки. Информация журналов должна достаточно надежно описывать почти все, что происходит в каждом компоненте. Но, как и выражения print, которые я добавлял в свои первые программы и которые могли давать большой объем вывода, то же самое может произойти и при протоколировании. Тщательно продуманные протоколирование и трассировка должны обеспечивать настройку типа и детализации вывода от любого компонента. Это позволяет операторам и разработчикам сначала выделить конкретный компонент с неправильным поведением, может быть даже на конкретном компьютере, а потом уже получить гораздо больше информации о том, что именно творится в нем, — без генерации уймы лишних данных в журнале, которые могут лишь отвлекать внимание от настоящей проблемы и существенно замедлять работу приложения.

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

Поддержка протоколирования в Windows Azure стала более зрелой в CTP-выпусках (Community Technology Preview). Изначально все протоколирование сводилось к выражениям print, захватываемым как текст в хранилище таблиц Windows Azure. Начиная с выпуска для PDC09 платформа Windows Azure стала предлагать гораздо более функциональную инфраструктуру протоколирования и трассировки, основанную на инфраструктуре Event Tracing for Windows (ETW).

Эта инфраструктура ETW поддерживается в ASP.NET через классы в пространстве имен System.Diagnostics. Производное от него пространство имен Microsoft.WindowsAzure.Diagnostics, в котором расширяются стандартные классы System.Diagnostics, позволяет использовать System.Diagnostics в качестве инфраструктуры протоколирования в среде Windows Azure. На рис. 1 показано, как реализуется ETW средствами Windows Azure Diagnostics.

Figure 1 High-Level Overview of Windows Azure Diagnostics
Рис. 1. Общая схема диагностики Windows Azure

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

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

Инфраструктура ETW сопоставляет TraceEventType с каждым событием, как показано на рис. 2. Чаще всего используются первые пять, и они указывают относительную важность трассировочного вывода. Заметьте, что типы Suspend, Resume и Transfer применяются в Windows Communication Foundation (WCF).

Рис. 2. Типы событий трассировки

TraceEventType Значение Описание
Critical 0x0001 Фатальная ошибка или крах приложения
Error 0x0002 Ошибка, после которой возможно восстановление
Warning 0x0004 Некритичная проблема — возможно, указывает на скорое появление более серьезных проблем
Information 0x0008 Информационное сообщение
Verbose 0x0010 Отладочная трассировка (например, детальная информация по потокам выполнения, параметрам и др.)
Start 0x0100 Запуск логической операции
Stop 0x0200 Прекращение логической операции
Suspend 0x0400 Приостановка логической операции
Resume 0x0800 Возобновление логической операции
Transfer 0x1000 Переход к новым операциям

Если вы ищете только по-настоящему скверные вещи, выберите захват лишь значений Critical и, возможно, Error. Ну а если вам нужно много информации о происходящем, выбирайте все, что над Verbose.

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

Вы можете подключать слушатели, источники и переключатели (switches) для программного задания разных уровней детализации вывода, но обычно это делается через конфигурационные файлы. Настраивать вывод можно в app.config (для рабочих ролей Windows Azure) или web.config (веб-ролей Windows Azure). Однако, как я подробно поясню позже, размещение настроек в ServiceConfiguration.cscfg позволяет конфигурировать параметры протоколирования и трассировки в процессе выполнения сервиса Windows Azure без повторного развертывания обновлений для выполняемого кода и даже без остановки сервиса. Windows Azure также предоставляет RESTful-интерфейс для удаленного управления некоторыми параметрами протоколирования. Использовать такой интерфейс позволяет Windows PowerShell.

Протоколирование, трассировка и отладочный вывод

Термины «протоколирование» (logging), «трассировка» (tracing) и «отладочный вывод» (debug output) иногда можно использовать как взаимозаменяемые. В этой статье я буду проводить различие между четырьмя типами, которые обобщенно называют диагностическим выводом (diagnostic output). Ниже они перечислены в порядке от наиболее детализированного до наименее подробного.

  • Отладочный вывод: Это информация, которая появляется только в отладочных сборках вашего приложения и исключается при компиляции выпускных сборок (release builds) (исходя из того, определен ли символ препроцессора DEBUG во время компиляции; этот символ Visual Studio определяет по умолчанию только для отладочных сборок). Как правило, в отладочный вывод включаются выражения вроде Assert, помогающие найти случаи, в которых код не удовлетворяет ожидаемым предусловиям, или даже дампы структур данных. Такой вариант помогает отлаживать алгоритмы в процессе тестирования.
  • Трассировка: Это выражения, помогающие отслеживать поток управления и состояние программы в процессе ее выполнения. Представьте, что вы запустили отладчик, пошагово проходите код и проверяете значения ключевых переменных в окне Watch. Выражения трассировки предназначены для выполнения тех же задач в случаях, когда подключить отладчик нельзя. В идеале, они должны предоставить достаточный контекст, который позволит увидеть, какой путь выбирается в каждой контрольной точке в приложении; ну а далее вы следуете ему в коде, читая выражения трассировки. Трассировка включается, когда в период компиляции определен символ препроцессора TRACE, и он может присутствовать как в отладочных сборках, так и в выпускных (производственных). (По умолчанию Visual Studio определяет TRACE в сборках обоих режимов, но вы, разумеется, можете это изменить.)
  • Протоколирование событий: Эти выражения предназначены для захвата важных событий в процессе выполнения программы, например начала транзакции или добавления элемента в базу данных. Протоколирование (регистрация) событий отличается от трассировки тем, что при этом вы получаете основные состояния программы, а не детализированный поток управления.
  • Протоколирование ошибок: Это особый случай протоколирования событий, при котором вы захватываете исключительные или потенциально опасные ситуации. К примерам можно отнести захват, во-первых, любых исключений, во-вторых, ситуаций, где вам не удается обратиться к какому-либо ресурсу на другой машине, который должен был бы быть доступен (ваше приложение, конечно же, должно корректно обрабатывать эти ситуации, но уведомлять о них), и в-третьих, случаев, где API-функции, от которых вы не ждете никаких ошибок, все же возвращают ошибки.

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

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

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

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

Трассировка и протоколирование в Windows Azure

Вы можете использовать готовые средства протоколирования в Windows Azure — они являются частью Windows Azure SDK. При использовании инфраструктуры протоколирования вроде Logger.NET, Enterprise Library, log4net или Ukadc.Diagnostics вы получаете некоторые преимущества. В этом случае в ваши сообщения, связанные с протоколированием, включается дополнительная структура, а также расширяются возможности настройки этих сообщений. Однако большинство таких инфраструктур еще не оптимизировано для работы в среде Windows Azure, а некоторые из них — нечто гораздо большее простой инфраструктуры протоколирования.

Для примеров кода в этой статье я решил остановиться на стандартных API протоколирования и трассировки Windows Azure с тонкой оболочкой вокруг них для поддержки динамической настройки. Возможно, вы сочтете полезным создать поверх этого кое-какие вспомогательные классы и инфраструктуру для реализации своей стратегии протоколирования и трассировки или присмотритесь к другим инфраструктурам с версиями для Windows Azure.

Когда вы создаете новый сервис в Windows Azure с помощью Visual Studio, в него сразу включается базовая поддержка протоколирования. В стереотипном коде Worker Role и Web Role, генерируемом шаблонами Windows Azure, содержится заранее сконфигурированный слушатель диагностической информации.

Чтобы включить простое протоколирование в сервисе Windows Azure, откройте новый проект в Visual Studio, используя шаблон Windows Azure Cloud Service (Visual C#). Присвойте этому проекту какое-либо имя. Я назвал его MSDNSampleLoggingService. Щелкните OK.

Запустится мастер New Cloud Service Project. В этом мастере добавьте в проекту одну рабочую роль (Worker Role) и одну веб-роль (Web Role). Переименуйте рабочую роль в LoggingWorkerRole, а веб-роль — в LoggingWebRole, затем щелкните OK. Visual Studio создаст проект.

В этот момент вы можете изучить сгенерированный код. Посмотрите app.config в проекте LoggingWorkerRole и обратите внимание на появление следующего кода:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.diagnostics>
    <trace autoflush="false" indentsize="4">
      <listeners>
        <add name="AzureDiagnostics" type="Microsoft.WindowsAzure.Diagnostics.DiagnosticMonitorTraceListener, Mi-crosoft.WindowsAzure.Diagnostics, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
      </listeners>
    </trace>
  </system.diagnostics>
</configuration>

Тем самым к вашему коду подключается стандартный слушатель диагностической информации Windows Azure, подразумевая, что любые сведения протоколирования и трассировки из рабочей роли будут направляться слушателю Windows Azure (DiagnosticMonitorTraceListener) до тех пор, пока вы явно не измените это. Аналогичную запись в web.config вы найдете и для веб-роли, созданной для этого сервиса.

Заглянув в файл WorkerRole.cs в проекте рабочей роли, вы также увидите следующую строку в методе OnStart:

DiagnosticMonitor.Start("DiagnosticsConnectionString");

а в методе Run вы заметите:

// This is a sample worker implementation. Replace with your logic.
Trace.WriteLine("LoggingWorkerRole entry point called", "Information");

Наконец, если вы посмотрите файл ServiceConfiguration.cscfg в корне Service, то обнаружите вот такую строку для обеих ролей:

<Setting name="DiagnosticsConnectionString" 
         value="UseDevelopmentStorage=true" />

Это сообщает слушателю Windows Azure, какую учетную запись хранилища следует использовать для сохранения информации протоколирования и трассировки. В данном случае информация протоколирования будет записываться в хранилище разработки на вашем локальном компьютере. Переключившись на учетную запись хранилища в облаке Windows Azure, вы сможете оправлять журналы в облачное хранилище. Вот пример формата для такого варианта из образца кода, который можно скачать для этой статьи:

<Setting name="DiagnosticsConnectionString" 
  value="DefaultEndpointsProtocol=https;AccountName=Xxxxxx;AccountKey=Yyyyyy" />

В AccountName и AccountKey нужно указать значения, соответствующие вашим конкретным учетной записи и ключу Azure. Вы получаете эту информацию с портала учетный записей хранилищ для своего сервиса по ссылке windows.azure.com. AccountName — это первая часть URL для таблицы и конечных точек хранилища больших двоичных объектов (blob) (т. е. часть до «.table.core.windows.net»). AccountKey является Primary Access Key (основным ключом доступа) к вашей учетной записи хранилища, закодированным по основанию base-64.

Заметьте: поскольку для диагностики используется другая учетная запись хранилища, вы можете выбрать сохранение своей диагностической информации отдельно от данных своего приложения. Для этого подготовьте отдельную учетную запись хранилища, щелкнув New Service на странице портала, выбрав Storage Account, а затем присвоив ей какое-либо имя (MyAppLogs, например). Кроме того, вы можете настроить группу привязки (affinity group), чтобы хранилище ваших журналов находилось в том же регионе, что и ваш сервис.

Теперь, когда мы совершили краткий экскурс по трассировке кода в сервисах Windows Azure, можно запустить ранее созданный проект веб-роли. Заметьте, что слушатель по умолчанию, предоставляемый Windows Azure, также направляет вывод в окно Output в Visual Studio для сборок в режиме Debug, поэтому вы увидите сообщение OnStart в отладочном окне:

Information: LoggingWorkerRole entry point called

Как быть, если вам нужно просматривать журналы после запуска сервиса? По умолчанию Windows Azure не сохраняет журналы в хранилище. Вы можете указать делать это, добавив в метод OnStart своей роли несколько строк кода:

TimeSpan tsOneMinute = TimeSpan.FromMinutes(1);
DiagnosticMonitorConfiguration dmc =
DiagnosticMonitor.GetDefaultInitialConfiguration();

// Transfer logs to storage every minute
dmc.Logs.ScheduledTransferPeriod = tsOneMinute;
// Transfer verbose, critical, etc. logs
dmc.Logs.ScheduledTransferLogLevelFilter = LogLevel.Verbose;    
// Start up the diagnostic manager with the given configuration
DiagnosticMonitor.Start("DiagnosticsConnectionString", dmc);

Добавив этот код в WorkerRole.cs и перезапустив сервис, вы заставите Windows Azure передавать журналы в хранилище разработки раз в минуту. Вы также можете выбрать вариант передачи журналов по запросу (см. код в admin.aspx.cs в моем приложении-примере) или использовать команды Windows PowerShell, описанные далее в этой статье. Помните: как только вы передали журналы в хранилище, с вас будет взиматься плата за используемое пространство хранилища, а за удаление ненужной информации отвечаете вы.

После записи журналов в хранилище Windows Azure вам потребуется какой-то инструмент для просмотра таблиц хранилища, где находятся журналы. Я использовал Cloud Storage Studio от Cerebrata (cerebrata.com). Cerebrata уже некоторое время поставляет утилиту Azure Diagnostics Manager; кроме того, на CodePlex (codeplex.com) есть бесплатные средства для просмотра хранилищ в облаке и диагностики. Журналы помещаются в таблицу WADLogsTable, показанную на рис. 3.

Figure 3 Logs Persisted in Development Storage
Рис. 3. Журналы, сохраняемые в хранилище разработки

Просматривая журналы в хранилище, вы заметите пару вещей. Во-первых, Windows Azure автоматически сопоставляет с каждым зарегистрированным событием кое-какую информацию: временную метку, счетчик тактов (обеспечивает более высокое разрешение показателей времени с гранулярность в 100 нс) и сведения о развертывании, роли и ее экземпляре. Это позволяет вам при необходимости сужать наборы журналов до конкретных экземпляров.

Во-вторых, с каждым событием ассоциируются Level и EventId. Level соответствует значениям на рис. 2 — события трассировки, зафиксированные как Information, будут иметь значение Level, равное 4, тогда как события, зарегистрированные как Error, — значение Level, равное 2. Универсальные события, посылаемые через Trace.WriteLine (как это делается в стереотипном коде), получат Level 5 (Verbose).

Значение EventId указывается вами. Базовый вызов Trace.WriteLine, показанный ранее, не позволяет сделать этого; вы должны использовать другие методы Trace для передачи EventId.

Выбор детализации трассировки и протоколирования

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

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

Ключевой подход здесь — не вызывать Trace напрямую, а использовать несколько экземпляров TraceSource — обычно по одному на каждое пространство имен. С TraceSource связывается SourceSwitch, который контролирует, включен ли источник и какой уровень детализации вывода требуется. Очень важно, что значения SourceSwitch задаются не при компиляции, а в период выполнения. В итоге вы можете включать или выключать диагностический вывод от различных частей своего приложения без его перекомпиляции и даже без развертывания заново другой версии.

WorkerDiagnostics.cs и WebDiagnostics.cs содержат конфигурацию источников трассировочной информации и переключателей в коде примера. Вот фрагмент:

// Trace sources
public TraceSource ConfigTrace;
public TraceSource WorkerTrace;
// Add additional sources here

// Corresponding trace switches to control 
// level of output for each source
public SourceSwitch ConfigTraceSwitch { get; set; }
public SourceSwitch WorkerTraceSwitch { get; set; }
// Add additional switches 1:1 with trace sources here

Затем в конфигурационном файле своей роли вы подключаете эти источники и и переключатели к слушателям, как показано на рис. 4. Здесь вы первым делом конфигурируете стандартный слушатель диагностической информации Windows Azure в качестве общего слушателя, чтобы на него можно было ссылаться в элементах <sources>. Далее настраиваете два источника: WorkerTrace и ConfigTrace. Кроме того, настраиваете соответствующие переключатели, которые позволяют регулировать уровень детализации вывода. ConfigTrace выдает самый детальный вывод, а WorkerTrace — только на уровне Errors.

Рис. 4. Настройка источников и слушателей трассировочной информации

<configuration>
  <system.diagnostics>
    <sharedListeners>
      <add type="Microsoft.WindowsAzure.Diagnostics.DiagnosticMonitorTraceListener, Microsoft.WindowsAzure.Diagnostics, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
        name="AzureDiagnostics">
        <filter type="" />
      </add>
    </sharedListeners>
    <sources>
      <source name="ConfigTrace">
        <listeners>
          <add name="AzureDiagnostics" />
        </listeners>
      </source>
      <source name="WorkerTrace">
        <listeners>
          <add name="AzureDiagnostics" />
        </listeners>
      </source>
    </sources>
    <switches>
      <add name="ConfigTrace" value="Verbose"/>
      <add name="WorkerTrace" value="Error"/>
    </switches>
    <trace>
      <listeners>
        <add name="AzureDiagnostics" />
      </listeners>
    </trace>
  </system.diagnostics>
</configuration>

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

При желании вы можете полностью отказаться от переключателя, просто добавив атрибут switchValue к элементу source, указывающий нужное значение переключателя (уровня детализации) для данного источника. Используемые вами значения переключателей разбираются из конфигурационного файла в одно из значений SourceLevel, определенных на рис. 5; на этом рисунке также показывается, как TraceEventType, передаваемый вами в вызовах TraceSource, взаимодействует с SourceLevel, заданным для источника.

Рис. 5. Уровни детализации для источника трассировочной информации и TraceEventType

Figure 5 Tracing Source Levels and TraceEventType

Вероятно, вы заметили, что SourceLevel — это просто битовая маска, которая в период выполнения комбинируется с TraceEventType логической операцией AND, чтобы определить, надо ли регистрировать данное событие. Чтобы получить комбинации вроде Warning и Activity Tracing, укажите числовое значение для битового поля как значение переключателя, не используя показанные на рис. 2 символьные значения.

Помимо переключателей, у слушателя может быть TraceFilter, который добавляет более сложную логику периода выполнения, определяющую, пропускать ли конкретное сообщение. Написание собственного TraceFilter выходит за рамки данной статьи, но полезный пример можно найти в документации на проект Ukadc.Diagnostics на CodePlex (ukadcdiagnostics.codeplex.com/wikipage?title=LoggingPrimer).

Смена уровня детализации в процессе выполнения

Это поддерживает трассировка в ASP.NET по умолчанию, и она прекрасно работает с сервисами, развернутыми в Windows Azure. Проблема в том, что вам нужна возможность изменения значений переключателя в период выполнения, а Windows Azure не позволяет просто заменять web.config или app.config без повторного развертывания сервиса. Универсальное ASP.NET-решение этой проблемы — использовать WebConfigurationManager для модификации конфигурационных значений, но в настоящее время Windows Azure не позволяет делать это для сервисов, развернутых в облаке.

Решение — отражать значения для нужных переключателей в ServiceConfiguration.cscfg. Windows Azure разрешает редактировать этот файл (или закачивать его новую версию) через портал разработки в процессе выполнения вашего сервиса. Но вам придется писать дополнительный код для осуществления этой работы.

По умолчанию код System.Diagnostics знает о настройках только в app.config или web.config, но ваши роли получат в период выполнения уведомление об изменениях в ServiceConfiguration.cscfg через события RoleEnvironmentChanging и RoleEnvironmentChanged. После этого вы можете выбрать либо перезапуск роли, либо простое обновление конфигурационного значения. Второй вариант — как раз то, что нужно для трассировочных переключателей. Перезапуск роли может разрешить кратковременные проблемы. В примере кода для этой статьи показано, как это сделать, добавив пару значений в ServiceConfiguration.cscfg (заметьте, что вы также должны отредактировать ServiceDefinition.csdef, который предоставляет схему) и включив дополнительный код в свои роли.

Тестирование в инфраструктуре разработки

Как обстоит дело с тестированием в инфраструктуре разработки, где отсутствует портал Windows Azure для изменения конфигурации, необходимый сервисам, которые развернуты в облаке? Во-первых, определите идентификатор развертывания, назначенный Windows Azure вашему выполняемому в инфраструктуре разработки сервису. Вы можете увидеть его, открыв UI инфраструктуры разработки из системного лотка в процессе выполнения сервиса. Это будет число наподобие 177.

  1. Перейдите в каталог, в который были помещены двоичные файлы вашего сервиса после компиляции, — обычно это \bin\debug или \bin\release в каталоге кода сервиса. Вы найдете копию ServiceConfiguration.cscfg, созданную после компиляции приложения.
  2. Используя текстовый редактор, измените этот файл так, чтобы использовать нужный вам трассировочный переключатель. Например, в образце кода измените значение WebTrace с Off на Verbose.
  3. Далее в командной строке Windows Azure SDK (Start | All Programs | Windows Azure SDK v1.1 | Windows Azure SDK Command Prompt) выполните команду:
csrun /update:NNN;ServiceConfiguration.cscfg

NNN — это идентификатор развертывания, назначенный Windows Azure (вы уже видели его). В инфраструктуре разработки он делает то же, что и кнопка Configure в портале разработки Windows Azure для сервисов, развернутых в облаке, — обновляет конфигурационные параметры и инициирует соответствующие события.

Другая диагностическая информация

Хотя в этой статье основное внимание уделяется табличным данным, которые ваше приложение записывает как журналы, используя System.Diagnostics, Windows Azure также умеет захватывать журналы IIS и Failed Request Tracing (ранее Failed Request Buffering, или FREB). Некоторые из них помещаются в хранилище больших двоичных объектов Windows Azure, а некоторые — в хранилище таблиц Windows Azure. На рис. 6 перечислены доступные журналы и места их хранения. Заметьте, что для журналов, не записываемых по умолчанию, обычно требуется вносить изменения в web.config или app.config, чтобы Windows Azure собирала информацию для таких журналов. Давайте подробнее рассмотрим один пример журнала, который не записывается по умолчанию; так будет проще понять, как все это работает.  

Рис. 6. Параметры стандартного протоколирования Azure

Тип журнала Формат хранилища Windows Azure Запись по умолчанию? Примечания
Журналы, генерируемые Windows Azure из вашего кода Таблица 4 Слушатель трассировки нужно указать в файле web.config или app.config file, как показано в коде примера. Хранятся в WADLogsTable
Журналы IIS 7.0 Двоичный объект 4 Только для веб-ролей. Хранятся в контейнере Blob в каталоге wad-iis-logfiles\<ID развертывания>\<имя веб-роли>\<экземпляр роли>\W3SVC1
Журналы Windows Diagnostic Infrastructure Таблица 4 Информация о самом диагностическом сервисе. Хранится в WADDiagnosticInfrastructureLogsTable
Журналы неудачных запросов Двоичный объект   Только для веб-ролей. Включаются выбором параметров трассировки под настройками system.WebServer в web.config. Хранятся в контейнере Blob в каталоге wad-iis-logfiles\<ID развертывания>\<имя веб-роли>\<экземпляр роли>\W3SVC1
Журналы событий Windows Таблица   Включаются изменением DiagnosticMonitor Configuration.WindowsEventLog при настройке начальной конфигурации. Хранятся в WADWindowsEventLogsTable. Подробности см. в блоге Стива Маркса (Steve Marx) (blog.smarx.com/posts/capturing-filtered-windows-events-with-windows-azure-diagnostics)
Счетчики производительности Таблица   Включаются изменением DiagnosticMonitor Configuration.PerformanceCounters. Хранятся в WADPerformanceCountersTable. В примере кода рабочей роли настраивается один из счетчиков производительности.
Аварийные дампы Двоичный объект   Включаются вызовом CrashDumps.EnableCollection. Хранятся в контейнере Blob в каталоге wad-crash-dumps. Так как ASP.NET обрабатывает большинство исключений, включение создания аварийных дампов обычно полезно только для рабочей роли.
Журналы собственных ошибок Двоичный объект   Выходят за рамки этой статьи, но полезный пример можно найти в блоге Нила Макензи (Neil Mackenzie) (nmackenzie.spaces.live.com/blog/cns!B863FF075995D18A!537.entry)

В качестве примера рассмотрим, как включить регистрацию FREB от IIS в вашей веб-роли. Чтобы увидеть это в действии, скачайте пример кода MSDNSampleLoggingService для этой статьи. Откройте web.config для LoggingWebRole и найдите раздел <system.webServer>. Заметьте, что строки, показанные на рис. 7, были добавлены в web.config, создаваемый в Windows Azure по умолчанию. Это приводит к регистрации неудач в обработке любых запросов, выполняемых более 15 секунд или возвращающих коды состояния от 400 до 599 (элемент failureDefinitions).

Рис. 7. Протоколирование неудачных запросов для LoggingWebRole

<tracing>
  <traceFailedRequests>
    <add path="*">
      <traceAreas>
        <add provider="ASP" verbosity="Verbose" />
        <add provider="ASPNET" 
             areas="Infrastructure,Module,Page,AppServices"
             verbosity="Verbose" />
        <add provider="ISAPI Extension" verbosity="Verbose" />
        <add provider="WWW Server"
             areas="Authentication,Security,Filter,StaticFile,CGI,Compression,Cache,RequestNotifications,Module" 
             verbosity="Verbose" />
      </traceAreas>
      <failureDefinitions timeTaken="00:00:15" statusCodes="400-599" />
    </add>
  </traceFailedRequests>
</tracing>

Если вы откроете about.aspx.cs в проекте LoggingWebRole, то заметите, что в метод PageLoad я добавил произвольную задержку на 18 секунд вот такой строкой кода:

System.Threading.Thread.Sleep(18000);

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

Чтобы увидеть журнал FREB, заново скомпилируйте приложение и разверните его в инфраструктуре разработки, а затем найдите контроллер этой инфраструктуры в системном лотке панели задач (возможно, вам придется щелкнуть кнопку Show Hidden Icons на панели задач, потому что зачастую такие значки скрываются как редко используемые). Щелкните его правой кнопкой мыши и выберите Show Development Fabric UI. Пока ваше приложение выполняется, этот UI будет показывать информацию о нем.

Раскройте Web Role и щелкните правой кнопкой мыши экземпляр роли (0). Выберите Open local store, чтобы открыть папку на локальной машине, куда были сохранены журналы (рис. 8). Внутри этой папки журналы находятся в папке \directory\DiagnosticStore. Это связано с тем, что веб-роль в примере кода сконфигурирована на хранение диагностической информации в хранилище разработке. Если вы перенастроите DiagnosticsConnectionString на учетную запись хранилища в облаке, сохраненные журналы будут в хранилище двоичных объектов, сопоставленном с этой учетной записью. Чтобы увидеть эти журналы, можно использовать Cloud Storage Studio для просмотра таких контейнеров.

Figure 8 Opening Logs Saved to Local Development Fabric Storage
Рис. 8. Открытие журналов, записанных в хранилище локальной инфраструктуры разработки

Управление диагностикой для выполняемого сервиса

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

Ну а если вдруг возникнет какая-то проблема? Вы же не хотите заново развертывать новую версию своего сервиса или ждать, когда проблема рассосется сама собой? Вы же, видимо, знаете, насколько бывает эффективна перезагрузка в устранении трудноуловимых проблем?

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

Ранее я описывал, как тонко настраивать уровень детализации тех журнальных данных, которые передаются в подсистему диагностики Windows Azure для конкретного TraceSource. Это своего рода заблаговременное редактирование информации, которая попадет в журналы. В этом разделе я покажу универсальные параметры диагностики в Windows Azure, определяющие, какая информация, передаваемая через TraceSource, попадает в постоянное хранилище.

Командлеты (cmdlets) Windows PowerShell позволяют управлять многими аспектами ваших сервисов, выполняемых в Windows Azure, в том числе их диагностикой. Вы запускаете их на локальной машине, и они соединяются через Интернет с серверами Windows Azure в облаке, выполняющими ваш сервис; после этого они позволяют получать информацию и подстраивать параметры. Windows PowerShell устанавливается с Windows 7, а для Windows Vista ее можно скачать по ссылке microsoft.com/powershell. Windows Azure Service Management CmdLets можно получить по ссылке code.msdn.microsoft.com/azurecmdlets; после этого следуйте инструкциям по их установке. Команды Windows Azure, относящиеся к диагностике, показаны на рис. 9.

Рис. 9. Командлеты, управляющие диагностикой в Windows Azure

Имя Описание
Get-ActiveTransfers Возвращает набор активных диагностических передач с сопутствующей информацией
Get-CommonConfigurationLogs Получает общие конфигурационные значения для всех буферов журналов. К ним относятся временной интервал, через который запрашиваются изменения в конфигурации, и размер буфера, выделенного под журналы в памяти
Get-DiagnosticAwareRoleInstances Возвращает список идентификаторов активных экземпляров ролей, для которых выполняется диагностический монитор.
Get-DiagnosticAwareRoles Перечисляет набор ролей, успешно запустивших минимум один диагностический монитор.
Get-DiagnosticConfiguration Получает конфигурацию буфера по указанному имени буфера (Logs, Directories, PerformanceCounters, WindowsEventLogs или DiagnosticInfrastructureLogs).
Set-CommonConfigurationLogs Задает общие конфигурационные значения для всех буферов протоколирования (журналов в памяти).
Set-FileBasedLog Задает конфигурацию буфера для журналов на основе файлов.
Set-InfrastructureLog Задает конфигурацию буфера для журналов, генерируемых нижележащей инфраструктурой диагностики Windows Azure. Журналы этой инфраструктуры полезны для выявления и устранения неполадок в самой диагностической подсистеме.
Set-PerformanceCounter Задает конфигурацию буфера для данных счетчика производительности, собираемых вашим сервисом.
Set-WindowsAzureLog Задает конфигурацию буфера для основных журналов Windows Azure, генерируемых вашим сервисом.
Set-WindowsEventLog Задает конфигурацию буфера для журналов событий Windows, генерируемых вашим сервисом.
Start-OnDemandTransfer Запускает по запросу передачу данных из указанного буфера. Это приводит к перемещению данных в хранилище Windows Azure (таблиц или двоичных объектов).
Stop-ActiveTransfer Останавливает активную передачу по запросу; вы должны передать идентификатор передачи.

Например, чтобы найти параметры текущей передачи для конкретного экземпляра роли, передайте идентификатор развертывания (deployment ID) (берется на портале разработки Windows Azure, где вы развертываете свой сервис), а также имя учетной записи хранилища и ключ, используемые вами для DiagnosticsConnectionString в app.config или web.config для роли сервиса (рис. 10). Заметьте, что Windows PowerShell запрашивает несколько недостающих обязательных параметров: экземпляр роли и имя нужного буфера; Logs — это стандартные журналы Windows Azure. Результат показывает, что уровень фильтра детализации — Verbose, а передачи запланированы на выполнение раз в минуту.

Figure 10 Diagnostics Configuration for a Running Service Using Windows PowerShell
Рис. 10. Настройка диагностики выполняемого сервиса с помощью Windows PowerShell

Чтобы изменить конфигурацию для этой роли, используйте командлет Set-DiagnosticConfiguration, как показано на рис. 11. Заметьте, что я сменил периодичность передач на каждые две минуты, а значение фильтра — с Verbose на Error, т. е. в постоянное хранилище будут отправляться лишь события, зарегистрированные как Error и Critical.

Figure 11 Changing Diagnostics Configuration from Windows PowerShell
Рис. 11. Изменение конфигурации диагностики из Windows PowerShell

Вероятно, самое полезное, что вы можете сделать удаленно из Windows PowerShell, — заставить немедленно передавать информацию журнала. На рис. 12 показано, как это делается.

Figure 12 Using Windows PowerShell to Initiate a Transfer of IIS Logs
Рис. 12. Инициация передачи журналов IIS через Windows PowerShell

Во-первых, я запрашиваю, идет ли в данный момент передача каких-либо журналов по запросу. В текущей реализации диагностики Windows Azure есть ограничение. Оно заключается в том, что единовременно возможна только одна передача по запросу конкретного типа. Если я вижу, что никакой передачи не выполняется, я запрашиваю таковую, передавая идентификатор развертывания сервиса, роль и экземпляр, тип журнала, который я хочу передать, и периодичность передачи этих данных. (В типах журналов Directories означает журналы на основе файлов, в том числе журналы IIS, а Logs — это журналы на основе таблиц Windows Azure, передаваемые через TraceSource.)

Я также передаю имя очереди уведомлений, куда подсистема диагностики Windows Azure отправит уведомление по окончании данной передачи. Экспериментальным путем я выяснил, что, если не передавать имя очереди уведомлений, то передача, похоже, не происходит. Обратно я получаю GUID, идентифицирующий запрос передачи. Затем я запрашиваю состояние запроса и вижу, что он опубликован, а значит, выполняется.

Текущая версия Windows Azure Service Management CmdLets, похоже, не сообщает, когда закончено выполнение запроса, но, запросив хранилище двоичных объектов для получения своего хранилища диагностической информации, вы увидите, что журналы ненадолго появляются в иерархии Containers (или в хранилище таблиц Windows Azure, если вы запрашивали передачу информации, сохраняемой в этом хранилище).

Заключение

Используя комбинацию настраиваемых конфигурационных параметров для компонентов TraceSource, которые вы определили в своем коде, и применяя Windows Azure Service Management CmdLets для перемещения информации в постоянное хранилище, вы получите полный контроль над детализацией диагностического вывода, получаемого от сервиса в Windows Azure.

Конечно, самое главное, что вы можете сделать для выявления и устранения неполадок в своем производственном коде, — продумать надежную стратегию диагностического вывода еще на самых первых этапах разработки, а затем четко следовать ей при дальнейшей разработке. Применение TraceSource и средств, предоставляемых Windows Azure для настройки детализации диагностического вывода, который передается из вашего приложения в хранилище, поможет вам получать ровно столько информации, сколько нужно для устранения возникшей проблемы.

Нет ничего хуже чувства, что код, который ведет себя хаотически на сервере, – это черный ящик, закрытый для вас. Надежный диагностический под и описанные в этой статье средства открою крышку этого ящика и позволят взглянуть внутрь.  

Майк Келли (Mike Kelly)  — консультант в области разработки ПО, помогает интегрировать ИТ-активы крупным корпорациям. Ранее в течение 15 лет работал в Microsoft в качестве разработчика ряда продуктов и был директором Emerging Practices в группе Engineering Excellence. С ним можно связаться по адресу himself@mikekellyconsulting.com.

Благодарю следующих технических специалистов за редактирование этой статьи:  Самит Мехротра (Sumit Mehrotra), Майкл Левин (Michael Levin) и Мэтью Кернер (Matthew Kerner) из корпорации Майкрософт, а также Нил Макензи (Neil Mackenzie) и Стивена Наги (Steven Nagy)