Отладка программного обеспечения является важной возможностью для разработчиков, но для отладки серверных архитектур требуется специальный подход. Бэкэнд Alexa Skill — это в большинстве случаев бессерверные функции, например, с использованием AWS Lambda. Отладка, мониторинг и тестирование являются главными проблемами, когда речь идет об отсутствии сервера, причем отладка является самой распространенной проблемой.
Бессерверные функции по своей конструкции очень сложно прикрепить к чему-либо. В результате мы наблюдаем новые решения для отладки без серверов, как локально, так и удаленно. В этом посте я представляю решение для мониторинга ваших навыков Алекса.
Мониторинг внутренностей нашего мастерства
Наш Skill Backend по своей природе основан на событиях. Это усложняет отладку, поскольку незначительные различия в содержании, формате или порядке события могут привести к большим различиям. Моделирование этих событий — потенциально большая разница, которая может затруднить воспроизведение и тестирование.
Когда вы сталкиваетесь с ошибкой в навыке, его надежное воспроизведение может быть полдела. Во многих средах, управляемых данными, основным фактором при воспроизведении ошибки является получение правильных входных данных. Перенос данных с производства на разработку может занять несколько часов (или даже дней) и может быть затруднен из-за безопасности, соответствия и других факторов. Инструменты производственной отладки позволяют разработчикам изучать ошибки «в дикой природе» на производстве, не тратя время и ресурсы на миграцию данных и не раскрывая конфиденциальные данные.
Существует множество инструментов, которые помогают нам отлаживать и отслеживать безсерверные приложения. Делая предыдущее исследование, в котором ищем, какой из них будет лучше следить за навыком Alexa, я выбрал Sentry
Пойдем глубже!
караул
Sentry — это компания с открытым исходным кодом, предоставляющая платформу для мониторинга приложений, которая помогает выявлять проблемы в режиме реального времени. Sentry — это сервис, который помогает отслеживать и устранять сбои в режиме реального времени. Сервер на Python, но он содержит полный API для отправки событий с любого языка, в любом приложении. Мы собираемся сосредоточиться на концепциях Sentry по контексту , хлебным крошкам и средам, чтобы обеспечить надлежащий мониторинг наших навыков.
Контексты
Sentry поддерживает дополнительный контекст с событиями. Часто этот контекст является общим для любого события, захваченного в его жизненном цикле, и включает в себя следующие компоненты:
Какие контексты мы будем использовать? эти:
- Пользователь : пользователь запросов Alexa
- Теги : это позволяет нам классифицировать и контекстуализировать каждый запрос, чтобы хорошо отслеживать и отслеживать все запросы. Мы настроим эти теги:
- request_id : идентификатор полученного запроса.
- application_id : идентификатор нашего навыка. Это позволяет нам сделать быстрый поиск, чтобы проверить все запросы одного навыка.
- session_id : идентификатор сеанса одного пользователя, использующего один навык. Это поможет нам отслеживать один сеанс одного пользователя.
- person_id : благодаря персонализации ваш навык может различать отдельных докладчиков в учетной записи. Так что, если Alexa признает текущий голос мы получим
User
иPerson
объекты , которые включают в себяperson_id
имущество.
Среды
В средах мы можем фильтровать события в зависимости от среды выполнения, т.е. dev / prod, а также в зависимости от выпуска нашего бэкэнда навыков. Вы можете фильтровать выпуски по среде, в которой они были развернуты. Например, выпуск, связанный с развертыванием QA и развертыванием Prod, появится в вашем представлении при фильтрации по QA и Prod.
Панировочные сухари
Sentry поддерживает концепцию Breadcrumbs, которая представляет собой список событий, которые произошли до появления проблемы. Часто эти события очень похожи на традиционные журналы, но также имеют возможность записывать более богатые структурированные данные. В эти хлебные крошки мы поместим соответствующие данные нашего навыка Alexa, такие как запросы (контексты, сеанс), ответы, общее время выполнения и т. Д.
Часовой в Нашем Умении
Я не собираюсь начинать с нуля в этом посте. Я буду повторно использовать проект, который использовал в своей пост-Alexa Skill, AWS CloudFormation y Модель сервера без приложений (SAM) .
Настройка Sentry в Нашем Умении
Прежде всего, мы должны добавить зависимость Sentry в pom.xml
файле:
XML
x
1
<dependency>
2
<groupId>io.sentry</groupId>
3
<artifactId>sentry</artifactId>
4
<version>1.7.30</version>
5
</dependency>
После этого нам нужно инициализировать клиент Sentry. Я инициализирую его в то же время, когда лямбда вызывается с помощью метода Sentry.init();
:
Джава
x
1
public class App extends SkillStreamHandler {
2
3
private static Skill getSkill() {
4
//SENTRY INITIALIZATION
5
Sentry.init();
6
return Skills.standard()
7
.addRequestHandlers(
8
new CancelandStopIntentHandler(),
9
new HelloWorldIntentHandler(),
10
new HelpIntentHandler(),
11
new LaunchRequestHandler(),
12
new SessionEndedRequestHandler(),
13
new FallbackIntentHandler(),
14
new ErrorHandler())
15
.addExceptionHandler(new MyExceptionHandler())
16
.addRequestInterceptors(new LogRequestInterceptor())
17
.addResponseInterceptors(new LogResponseInterceptor())
18
// Add your skill id below
19
//.withSkillId("[unique-value-here]")
20
.build();
21
}
22
23
public App() {
24
super(getSkill());
25
}
26
27
}
Когда Sentry.inti()
вызывается, он будет читать sentry.properties
файл ресурсов, который имеет следующие свойства:
- dsn : уникальный URL вашего проекта Sentry. Вы можете получить его онлайн в своем проекте Sentry. Например, ttp: //fasdfasd@sentry.io/asdfas
- release : чтобы лучше контролировать наш навык, было бы хорошо, чтобы это свойство было установлено для фильтрации, когда что-то происходит
- окружение : также важно установить из-за знания текущей среды исполнения. Например: dev, prod.
- stacktrace.app.packages : пакет Java, который вы хотите отслеживать. В нашем случае
com.xavidop,alexa.helloworld
Sentry в наших навыках перехватчиков
После добавления зависимости, теперь мы собираемся сосредоточиться на наших двух перехватчиках. LogRequestInterceptor
и LogResponseInterceptor
. Почему эти двое?
- У
LogRequestInterceptor
нас есть полученный запрос, в котором есть вся информация, необходимая для создания наших тегов Sentry User и Sentry для мониторинга текущего запроса:
Джава
xxxxxxxxxx
1
public class LogRequestInterceptor implements RequestInterceptor {
2
3
static final Logger logger = LogManager.getLogger(LogRequestInterceptor.class);
4
5
public void process(HandlerInput input) {
6
TimeUtilities.start = new Date().getTime();
7
HashMap<String, String> data = new HashMap<String,String>();
8
//SETTING RELEVANT TAGS TO MAKE SEARCHES IN SENTRY
10
Sentry.getContext().addTag("request_id", input.getRequestEnvelope().getRequest().getRequestId());
11
Sentry.getContext().addTag("application_id", input.getRequestEnvelope().getSession().getApplication().getApplicationId());
12
Sentry.getContext().addTag("session_id", input.getRequestEnvelope().getSession().getSessionId());
13
if(input.getRequestEnvelope().getContext().getSystem().getPerson() != null){
14
Sentry.getContext().addTag("person_id", input.getRequestEnvelope().getContext().getSystem().getPerson().getPersonId());
15
}
16
17
18
//SET EXTRA USEFUL DATA FOR THE BREADCRUMB
19
data.put("Request", input.getRequestEnvelope().getRequest().toString());
20
data.put("context", input.getRequestEnvelope().getContext().toString());
21
data.put("Session", input.getRequestEnvelope().getSession().toString());
22
data.put("Version", input.getRequestEnvelope().getVersion());
23
24
//CREATING THE USER OF THIS REQUEST FOR A FUTURE SEARCHES
26
HashMap<String, Object> userData = new HashMap<String,Object>();
27
userData.put("device_id",input.getRequestEnvelope().getContext().getSystem().getDevice().getDeviceId());
28
Sentry.getContext().setUser(
29
new UserBuilder()
30
.setData(userData)
31
.setUsername(input.getRequestEnvelope().getSession().getUser().getUserId()).build()
32
);
33
34
Sentry.getContext().recordBreadcrumb(
35
new BreadcrumbBuilder()
36
.setLevel(Breadcrumb.Level.DEBUG)
37
.setTimestamp(new Date())
38
.setData(data)
39
.setMessage("New request recieved").build()
40
);
41
42
logger.info(input.getRequest().toString());
43
}
44
}
Здесь LogResponseInterceptor
у нас есть ответ, который мы собираемся отправить, и мы можем вычислить время в миллисекундах текущего выполнения. Поскольку это последние строки нашего навыка во время выполнения, одной из основных задач этого перехватчика является отправка всего события Sentry в облако с помощью метода Sentry.capture()
. Наконец, мы очищаем все Sentry.clearContext()
для будущих запросов Alexa:
Джава
x
1
public class LogResponseInterceptor implements ResponseInterceptor {
2
static final Logger logger = LogManager.getLogger(LogRequestInterceptor.class);
4
5
public void process(HandlerInput input, Optional<Response> output) {
6
TimeUtilities.end = new Date().getTime();
7
HashMap<String, String> data = new HashMap<String,String>();
8
//GET THE RESPONSE AND PUT IT AS DATA OF THE BREADCRUMB
10
data.put("Response", output.get().toString());
11
//GET THE TIME OF THE EXECUTION
13
long time = TimeUtilities.end - TimeUtilities.start;
14
data.put("Overall Time", String.valueOf(time) + "ms.");
15
//CREATE A BREADCRUMB WITH THE INFO OF THE RESPONSE
17
Sentry.getContext().recordBreadcrumb(
18
new BreadcrumbBuilder()
19
.setLevel(Breadcrumb.Level.DEBUG)
20
.setTimestamp(new Date())
21
.setData(data)
22
.setMessage("New response").build()
23
);
24
Sentry.capture("request - " + Sentry.getContext().getTags().get("request_id"));
26
//CLEAN CONETXT FOR NEW REQUESTS
27
Sentry.clearContext();;
28
logger.info(output.toString());
30
}
31
}
Вход в наш навык с часовым
Одной из наиболее важных задач в архитектуре без сервера является журнал. Важно знать, что произошло во время казни. В этом примере мы будем использовать вспомогательный класс с именем, у LogUtilities
которого есть только один метод, у log(String toLog)
этого метода есть две задачи:
- Войдите в систему с помощью библиотек log $ j2 или lambda4j, чтобы получить структурированные файлы журналов.
- Создайте хлебную крошку Sentry, которая будет добавлена к остальным хлебным крошкам и будет доступна на нашей консоли Sentry как часть события, сгенерированного запросом. Это поможет нам отслеживать, что произошло в запросе.
Таким образом, с этим классом вы можете называть его везде, где хотите отставать и добавлять свои хлебные крошки. Это поможет вам контролировать и иметь хорошее отслеживание ваших навыков.
Поймать исключения в нашем мастерстве с помощью Sentry
Последнее, но не менее важное, когда мы говорим о мониторинге, это исключения.
В нашем Alexa Skill у нас есть одно место, чтобы поймать все исключения. Это MyExceptionHandler
наш обработчик исключений. Когда у нас есть исключение, LogResponseInterceptor
оно не будет выполнено. Вот почему здесь мы фиксируем исключение с помощью Sentry, а также очищаем его контекст:
Джава
x
1
public class MyExceptionHandler implements ExceptionHandler {
2
3
public boolean canHandle(HandlerInput input, Throwable throwable) {
4
return throwable instanceof Exception;
5
}
6
8
public Optional<Response> handle(HandlerInput input, Throwable throwable) {
9
//CAPTURING THE EXCEPTION
10
Sentry.capture(throwable);
11
//CLEANING CONTEXT
12
Sentry.clearContext();
13
return input.getResponseBuilder()
15
.withSpeech("An error was encountered while handling your request. Try again later.")
16
.build();
17
}
18
}
Сила Стража
Теперь мы установили наш навык с Sentry. Давайте отправим запрос Alexa в его серверную часть, чтобы посмотреть, что произошло, и затем мы проверим консоль Sentry:
ВОТ ЭТО ДА! У нас новая запись!
Теперь нажмите на элемент и посмотрим, что внутри:
Здесь у вас есть информация о событии: время отправки запроса Alexa и все теги. Каждый тег кликабелен для фильтрации в зависимости от ваших потребностей.
Если прокрутить вниз, мы увидим хлебные крошки. Это означает быстрый взгляд на то, что произошло во время этого запроса Alexa:
ПРИМЕЧАНИЕ. Данные запроса и ответа были удалены для этого примера.
И затем, если мы продолжим прокрутку вниз, мы увидим информацию о пользователе (имя пользователя Alexa и устройство Alexa) и информацию об используемом Sentry SDK.
Если у нас есть исключение, мы также можем увидеть в Sentry Dashboard:
Мы также можем увидеть всю информацию о выполнении, нажав на событие:
Вы можете увидеть полную трассировку стека, нажав на кнопку Raw:
Прежде чем закончить эту тему, я хотел бы добавить, что с помощью Sentry вы можете выполнять поиск любого тега, пользователя, среды, выпускаемой версии.
Например:
- Дайте мне все запросы Alexa, которые пришли от одного пользователя и одного навыка:
- request_id: amzn1.echo-API.request. [уникальное-значение-здесь] application_id: amzn1.ask.skill. [уникальное-значение-здесь]
- Дайте мне все запросы Alexa, которые пришли с одной сессии:
- session_id:. amzn1.echo-API.session [уникальное значение, здесь]
- Дайте мне все запросы Alexa, которые пришли из текущей версии:
- выпуск: XXX
Вы можете сохранить эти поиски как быстрые, и они будут доступны в один клик.
Заключение
С помощью Sentry мы быстро перешли от нулевого знания к пониманию ошибки. Использование правильных инструментов в нужное время может очень помочь в решении подобных проблем.
Я сделал этот пример на Java, но вы можете использовать его на других языках, которые поддерживает Alexa, потому что Sentry доступен во многих языках программирования, таких как NodeJS, Python, Java, Kotlin, C #, PHP, Ruby, Go, iOS Android, и т.п.
Вы можете посмотреть всю документацию Sentry здесь .
Что касается цен, у Sentry есть план развития, который включает в себя 5000 событий в месяц. В нашем случае 5000 запросов Alexa от нашего навыка. Вы можете увидеть планы здесь .
Вот и все, ребята! Вы можете найти весь код в моем GitHub .
Надеюсь это будет полезно! Если у вас есть какие-либо сомнения или вопросы, не стесняйтесь связаться со мной или оставить комментарий ниже!