Автоматизация трассировки Sentry в TypeScript с помощью декораторов!

Эта статья посвящена следующим темам:

  1. Что такое Сентри
  2. Обзор Sentry Tracing
  3. Понимание декораторов методов TypeScript
  4. Проектирование декоратора трассировки Sentry
  5. Выполнение
  6. Тестирование и проверка
  7. Преимущества и соображения
  8. Заключение и рабочая демонстрация

1. Что такое Сентри

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

Давайте посмотрим, что именно представляет собой сентри-трейсинг:

2. Обзор Sentry Tracing

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

Визуально трассировка выглядит так:

Не буду вдаваться в подробности, но вот краткий обзор транзакций и спанов в трассировке Sentry:

  • Транзакция. Транзакция — это самый высокий уровень операций в вашем приложении, который вы хотите измерять и отслеживать. Он представляет собой единую логическую единицу работы, которую выполняет ваше приложение, например, обработку веб-запроса, обработку фонового задания или выполнение запроса к базе данных. Транзакции могут быть вложены друг в друга, образуя древовидную структуру, представляющую полный путь выполнения.
  • Span. Диапазон представляет собой одну операцию внутри транзакции. Это способ измерить время, затрачиваемое определенной частью вашего кода, например, вызовом функции, запросом к базе данных или HTTP-запросом. Промежутки могут быть вложены друг в друга, чтобы представить полный стек вызовов вашего приложения.

если вы хотите прочитать более подробное руководство, ознакомьтесь с официальной документацией по трассировке здесь:

https://docs.sentry.io/product/sentry-basics/tracing/distributed-tracing/

3. Понимание декораторов методов TypeScript

Декораторы методов TypeScript применяются к методам внутри класса. Декораторы методов получают три параметра: целевой объект (прототип), имя метода и дескриптор свойства.

Они предоставляют способ расширить или изменить поведение метода без изменения его исходной реализации.

Чтобы определить декоратор метода, вы используете символ @, за которым следует имя декоратора непосредственно перед объявлением метода. Вот пример:

4. Дизайн декоратора трассировки Sentry

Хотя Sentry Tracing имеет обширный список функций, наиболее важной из них является возможность отслеживать и синхронизировать поток вызовов различных функций с помощью декораторов и вложенным образом. Для этого мы создадим декоратор @SentryTraced(), который будет использоваться для аннотирования всех методов, которые нам нужно отслеживать, вот простой пример:

5. Реализация

Во-первых, как следует из названия, нам нужно начать с создания шаблона декоратора, и вот он:

Я не буду вдаваться в подробности, но этот исходный код заставляет декоратор работать,

Кроме того, при запуске этого кода обязательно установите “experimentalDecorators”: true в файле ts-config.json.

Теперь пришло время добавить основную логику в декоратор, то есть трассировку Sentry.

Объединение двух представленных выше концепций, логики трассировки часового и стандартного шаблона декоратора метода приведет к следующей реализации:

Разберем все подробно:

const className = this.constructor.name;
const methodName = propertyKey;
const op = `Operation ${className}#${methodName}`;
const description = `Description for ${className}#${methodName}`;

Эти строки извлекают имя класса (className) и имя метода (methodName), к которому применяется декоратор. Они используются для создания операции (op) и описания (description) диапазона или транзакции, которые будут использоваться в целях отслеживания.

const intermediaryFunction = async () => {
  // ...
};

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

const scope = Sentry.getCurrentHub().getScope();
const contextTransaction = scope?.getSpan() || scope?.getTransaction();
const transactionOrSpan = contextTransaction ||
  Sentry.startTransaction({
    op,
    description,
    name: `Transaction for ${className}#${methodName}`,
  });
const newSpan = transactionOrSpan?.startChild({
  op,
  description,
});

Sentry.configureScope((scope) => {
  scope.setSpan(newSpan);
});

Эти строки извлекают текущую область действия из системы отслеживания ошибок Sentry. Область содержит информацию о текущем активном диапазоне или транзакции. Если текущей области, диапазона или транзакции нет, новая транзакция (корневой узел) создается с использованием метода Sentry.startTransaction(). Новый диапазон запускается как дочерний элемент текущей транзакции или диапазона с использованием метода startChild(). Затем метод setSpan() используется для установки вновь созданного диапазона в качестве текущего диапазона в области Sentry.

const result = original.apply(this, args);
if (isPromise(result)) {
  // in case the function is a promise we need to resolve it and also
  // reset the current context to the parent node for the next sibling function call
  return result.then((resultFromFunction: any) => {
    Sentry.configureScope((scope) => {
      scope.setSpan(transactionOrSpan);
    });
    newSpan?.finish();
    // finish the transaction if it's a root node
    if (!contextTransaction) {
      transactionOrSpan.finish();
    }
    // return the result of the original function
    return resultFromFunction;
  });
} else {
  // same for a normal function but we just do it before returning the value
  // context to the parent node for the next sibling function call
  Sentry.configureScope((scope) => {
    scope.setSpan(transactionOrSpan);
  });
  newSpan?.finish();
  // finish the transaction if it's a root node
  if (!contextTransaction) {
    transactionOrSpan.finish();
  }
  return result;
}

Эта часть вызывает исходный метод (без ожидания), а затем проверяет, является ли ответ обещанием или нет. Мы делаем это потому, что методы, которые будут декорированы, могут либо возвращать обещание, либо просто значение, например:

@SentryTraced()
async method1(){ return 'something'; } // returns a promise

@SentryTraced()
method2(){ return 'something'; } // returns a value

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

Тестирование реализации

Для тестирования нам нужно создать класс с кучей декорированных методов, вот пример:

Запуск этого кода генерирует следующие выходные данные трассировки часового:

Заключение и рабочая демонстрация

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

Этот декоратор можно упаковать в библиотеку npm и использовать в любой кодовой базе, требующей трассировки. Вы можете найти реализацию на моей странице GitHub здесь: https://github.com/alexcambose/sentry-traced

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

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

Дополнительные материалы на PlainEnglish.io.

Подпишитесь на нашу бесплатную еженедельную рассылку новостей. Подпишитесь на нас в Twitter, LinkedIn, YouTube и Discord .