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

Замечания

Обработка исключений - это тема, глубина которой выходит за рамки данной статьи. Код, на который я здесь ссылаюсь, не рассчитан на то, чтобы быть максимально быстрым или эффективным. Это сделано в интересах демонстрации каждой из функций и создания удобной демонстрации, которую читатели могут затем улучшить в своих целях. Вы всегда можете связаться со мной для получения разъяснений или совета по этим темам; Я также приветствую вас, чтобы вы указали на ошибки, ошибки или даже отправили запросы на вытягивание в репозиторий по своему усмотрению.

Моя цель - изучить кроссплатформенные возможности .NET Core и показать вам, как обработка исключений может быть интегрирована со следующими инструментами:

· .NET Generic Host - это универсальный пакет для обработки конфигурации, внедрения зависимостей, ведения журнала и других проблем, связанных с запуском приложений. Если вы хотите разместить асинхронные службы, вы можете сделать это прямо сейчас, затратив очень мало времени на настройку. Для нас это упростит большую часть шаблонов, необходимых для прототипирования интересных сценариев использования АОП.

· Azure Application Insights - это большой набор сервисов, направленных на мониторинг производительности в режиме реального времени. Вы можете отправлять события, метрики и всевозможные пользовательские измерения в Azure, а затем сразу же запрашивать их через Log Analytics. Целью здесь будет обработка наших исключений путем их регистрации в облаке, где мы можем регулярно получать снимки состояния приложения.

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

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

Прежде, чем мы начнем

Читатели, желающие продолжить, должны предпринять следующие шаги для настройки своей среды:

Понимание обработки исключений с учетом шаблонов

Обработчик исключений обычно требует, чтобы вы использовали некоторую форму операторов try { } catch { } finally { }, которые требуют, чтобы каждый класс знал свой собственный контекст достаточно хорошо, чтобы либо всплыть, либо проглотить, либо обработать исключение, которое происходит изнутри. Неправильно обработанное исключение может легко вызвать прерывание обслуживания или аналогичное неудобство для конечного пользователя. Это возлагает на любую команду разработчиков бремя документации и знаний, чтобы понять общую архитектуру обработки ошибок, выходящую за рамки какой-либо конкретной функции.

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

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

Обработка исключений - основная функция пространства имен PostSharp, и я рекомендую прочитать ее, прежде чем продолжить. Для базовой реализации нам предоставлены следующие объекты:

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

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

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

У этого класса есть несколько интересных моментов:

  • Во-первых, он наследует класс OnExceptionAspect, который предоставляет обработчик исключений.
  • Затем он перехватывает исключения, созданные с помощью OnException( MethodExecutionArgs args ), и отслеживает их состояние с помощью MethodExecutionArgs, упомянутого ранее.
  • Наконец, он создает строку, содержащую контекст, включая аргументы, объявляющий тип, универсальные шаблоны и имя метода. Они добавляются в индекс исключения ”Context” в Свойстве данных.

Второй пример из официальных примеров - это атрибут «Сообщить и проглотить исключение». Этот взаимодействует напрямую с предыдущим атрибутом, считывая ”Context”, добавленный обработкой, выполненной в атрибуте AddContextOnException. Несколько замечаний:

  • Аспект объявляет [AspectTypeDependency], который является способом обработки зависимостей аспектов для одной и той же цели. В этом случае он объявляет, что он должен идти после атрибута AddContextOnException, если он применяется к той же цели.
  • Он проверяет наличие additionalContext путем проверки свойства Data исключения любой информации, добавленной ранее через StringBuilder.

Применение нашего понимания с помощью Azure

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

  1. Для этого потребуется учетная запись Azure, но к счастью, она поставляется с бесплатной пробной версией и ее можно быстро настроить. Если это ваш первый раз, то Я рекомендую это видео, в котором показано, как завершить весь процесс всего за несколько секунд.
  2. Создайте новый ресурс Application Insights через Azure. Возьмите «Инструментальный ключ» со страницы обзора.
  3. Скопируйте свой «Идентификатор приложения» из меню «Доступ к API» своего ресурса Application Insights.
  4. Создайте ключ API и скопируйте его. Это можно сделать в том же меню «Доступ к API». Предусмотрите все три варианта [Read Telemetry, Write Annotations, and Authenticate SDK].

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

Интеграция PostSharp с Azure

Целью здесь будет запись настраиваемых событий в Azure, описывающих исключение каждый раз, когда оно обрабатывается. Мы можем ограничить это только двумя классами, одним для Azure и одним для обработчика исключений, используя OnExceptionAspect, о котором мы узнали ранее из примеров PostSharp. Во-первых, давайте напишем Аспект, чтобы сделать следующее:

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

Давайте разберем этот класс, чтобы увидеть, где конкретно был включен Azure:

Здесь мы видим, что интеграция с Azure позволяет нам передавать словарь свойств (аналогичный структуре JSON) в настраиваемое событие, и все, что нам нужно сделать, это дать ему контекст. В этом случае контекст - это «Исключение». Вот и все! Единственная другая конфигурация, которая нам нужна для подключения к Azure, - это добавить два файла в наш проект. Monitor.cs и ApplicationInsights.config

В приведенном выше коде нам просто нужно заменить «Your Key Here» на инструментальный ключ, который мы извлекли из Azure ранее. Затем мы добавляем этот ApplicationInsights.config файл в папку, содержащую наш .csproj.

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

Снова замените «Your Key Here» на «Instrumentation Key», предоставленную Azure. Обратите внимание: читатели должны хранить свои ключи в секретах пользователя или в более безопасном месте. Этот код используется только для примеров, поэтому, пожалуйста, не передавайте ключи напрямую в репозиторий!

На этом этапе аспект готов к отправке данных в Application Insights. Если вы используете код, представленный здесь, добавьте к любому методу [ServiceExceptionDetour], а затем выбросите исключение из этого декорированного метода. Например:

Здесь вы можете видеть, что я использую прекрасный ExceptionGremlin из Библиотеки HouseOfCat. Автор предоставляет ряд полезных инструментов, например, фаззинг базовых исключений. Каждый раз, когда я использую этот метод CauseException, он будет маршрутизироваться через обработчик исключений, а его данные будут переданы в Azure. Вы также заметите, что он оформлен как [EntryPoint], что означает, что он безопасно вводится из многих потоков в модели актора, несмотря на то, что он частный. При таком использовании важно понимать срок службы и объем аспектов. Однако для наших целей мы можем не обращать внимания на любые проблемы с производительностью, потому что исключения и так дороги, и мы не пытаемся быть здесь изящными.

Журнал аналитики

Если вы перейдете к Log Analytics в своем ресурсе Application Insights, вы сможете запросить customEvents таблицу, чтобы узнать, все ли работает. У вас должно быть customDimensions в каждой записи, которая содержит данные, которые вы прикрепили к событию из Exception, когда оно было обработано.

Если вы новичок в Log Analytics, я рекомендую ознакомиться с официальным руководством Язык запросов Kusto (KQL) с нуля на Pluralsight. Вот пример запроса для нашего текущего набора настраиваемых событий:

Графана

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

После регистрации вам нужно будет настроить панель управления для работы с Azure.

  1. Установите подключаемый модуль Azure Monitor на свой экземпляр Grafana.
  2. Откройте панель управления и перейдите в раздел Конфигурация ›Источники данных. Добавьте источник данных для Azure Monitor.
  3. Заполните два поля «Ключ API» и «Идентификатор приложения» для Application Insights. Мы получили эти ключи ранее, когда настраивали наш ресурс Azure.
  4. Нажмите «Сохранить и проверить», чтобы проверить, работает ли соединение.

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

Гистограмма:

Датчик и аннотация:

Куда я могу пойти дальше?

На этом этапе вы можете наблюдать за потоком данных на панели управления Grafana в режиме реального времени! Теперь этот конвейер можно использовать для отслеживания любых метрик. PostSharp предлагает полный набор инструментов для внедрения поведения до и после выполнения в методы, свойства / поля и события. Везде, где вы хотите собирать метрики, не переписывая свои старые классы, стоит рассмотреть возможность использования Аспекта для предоставления этого совета.

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

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

Кредит

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

Для генерации исключения здесь использовалась Библиотека HouseOfCat. Я рекомендую проверить их библиотеку Rabbit MQ, если вы регулярно используете очереди сообщений.

Заключительные замечания

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

Надеюсь, эта статья раскрыла всю мощь аспектно-ориентированного программирования в архитектуре .NET Core. Это процесс обучения для всех нас, и я приглашаю вас поделиться им или сотрудничать в любое время. Спасибо, что нашли время прочитать.