Русский
Русский
English
Статистика
Реклама

Crash-crash, baby. Автоматический мониторинг фатальных ошибок мобильных приложений

Всем привет! Меня зовут Дмитрий, я релиз-инженер вкоманде CI/CD Speed Авито. Вот уже несколько лет мы сколлегами отвечаем за всё, что связано срелизами наших мобильных приложений и не только. Впрошлый раз я рассказывал онашей системе релизов мобильных приложений наоснове контракта. Сегодня речь пойдет отом, как мы автоматизировали сбор информации изFirebase оновых фатальных ошибках вмобильных приложениях.



Проблематика


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


Раньше, как и многие нарынке мобильных приложений, мы использовали Fabric, длякоторого vadimsmal и YourDestiny написали очень удобный клиент Fabricio. Набазе этого клиента унас была создана система мониторинга, которая заводила Jira-задачи нановые фатальные ошибки, искала ответственных поGit-Blame и сообщала обошибках вcпециальный слак-канал.


Нокомпания Google решила прекратить развитие проекта Fabric, объявила дату закрытия и предложила всем желающим мигрировать наих платформу Firebase, что мы благополучно и сделали.


Система автоматического мониторинга крашей перестала работать, и нам пришлось возвращаться кпостоянному ручному мониторингу. Припервичном изучении документации выяснилось, что уFirebase нет публичного API дляполучения необходимой информации, и придётся искать обходные пути.


Получаем данные


Google Cloud Functions


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


Исследование документации Firebase привело нас кGoogle Cloud Functions или же облачным функциям. Это serverless FaaS отGoogle, который позволяет запускать ваш код воблачной инфраструктуре Google. УFirebase-Crashlytics есть встроенная интеграция соблачными функциями (намомент написания статьи данная функциональность помечена как deprecated). Вы можете написать call-back наодин изтрёх crashlytics-ивентов и дальше обрабатывать его как вашей душе угодно. Особенно нас интересуют два ивента onNew(новое событие crashlytics) и onVelocityAlert (резкий рост события crashlytics).



Вголове сразу же родилась схема. Настраиваем интеграцию Firebase-Google Cloud Functions, шлём оттуда все новые краши сразу всвой сервис, и там уже обрабатываем. Берём пример издокументации, вносим несколько доработок и получаем следующий код наJS который загружаем вGoogle Cloud:


const functions = require('firebase-functions');const rp = require('request-promise');function sendEvent(event) {    return rp({        method: 'POST',        uri: functions.config().crashlytics.crash_collector_url,        body: event,        json: true,    });}exports.NewIssueEvent = functions.crashlytics.issue().onNew(async (issue) => {    await processEvent(issue, 'NewIssueEvent')});exports.RegressedEvent = functions.crashlytics.issue().onRegressed(async (issue) => {await processEvent(issue, 'RegressedEvent')});exports.VelocityAlertEvent = functions.crashlytics.issue().onVelocityAlert(async (issue) => {await processEvent(issue, 'VelocityAlertEvent')});const processEvent = async (event, type) =>{    if (isActualEvent(event)) {        await sendEvent(event);        console.log(`Posted ${type} ${event.issueId} successfully to crash collector`);    }    else{        console.log(`It's old event or not Avito. Do nothing`);    }}const isActualEvent = (event) =>{    const {appInfo} = event;    const {appName, latestAppVersion} = appInfo;    const version = latestAppVersion &&  parseFloat(latestAppVersion.split(' ')[0]);    console.log(`Event appName: ${appName} version: ${version}`);    return appName === 'Avito' && version > 60.0}

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


Но втекущей реализации нам не хватает данных. ВFirebase-Crashlytics есть fatal события (собственно фатальные ошибки-краши) и non-fatal (остальные события которые по той или иной причине логируются в crashlytics). Все летящие кнам ивенты насобытие onNew не имеют признака фатальности, ктому же нам хотелось как-то фильтровать события поколичеству затронутых пользователей и частоте возникновения, но этой информации всобытиях нет.


BigQuery


Google позволяет экспортировать данные изFirebase вBigQuery. BigQuery облачное хранилище, предоставляющее удобную платформу дляхранения и обработки данных. На момент исследования всередине 2019года был доступен только один тип синхронизации cFirebase Batch Table.


Нужно отметить ключевые особенности данного типа синхронизации:


  1. Синхронизация происходит раз всутки, приэтом нет гарантии, когда она будет завершена.
  2. Нельзя настроить тип экспортируемых событий экспортируется и fatal и non-fatal.
  3. Чем дольше живёт таблица, тем больше вней данных (ваш кэп) и тем дороже стоят услуги хранения.

Дорабатываем изначальную схему:



После получения ивента внашем сервисе идём вBigQuery и получаем недостающую информацию: признак фатальности, число задетых пользователей и так далее. При этом запросы кBigQuery отправляем не накаждый новый ивент, а периодически. Длянас оптимальная частота запросов раз вдень после 17:00, так как заэто время выгрузка данных изFirebase-Crashlytics вBigQuery успевала завершиться, и можно было получить информацию повсем необработанным ивентам простым запросом:


SELECT issue_id, is_fatal, COUNT(*) as crashes_counter, COUNT(DISTINCT installation_uuid) AS affected_users FROM `android.firebase_crashlytics.{table}` WHERE issue_id in ( {issues_id_string} ) GROUP BY issue_id, is_fatal LIMIT 1000

Внимательный читатель может заметить, что тут образовывается временной лаг между фактическим появлением краша и получением нами информации онём. Чтобы не пропускать редкие, но действительно важные краши, которые резко растут и задевают сразу много пользователей, унас по-прежнему оставалось событие onVelocityAlert вGoogle Cloud Function. Подокументации это событие вызывается исключительно нафатальные ошибки вработе приложения, если ошибка привела ксбою N сеансов пользователей запоследний час. Пофакту же onVelocityAlert не работало, мы зарепортили это вGoogle, нас внесли вовнутренний трекер, и наэтом всё.


Слак


Нас вполне устраивал временной лаг приполучении обычных фатальных ошибок, но совсем не хотелось пропустить пожар. Впоисках возможности получения VelocityAlert мы пришли кинтеграции Firebase-Crashlytics и слака. Вконсоли Firebase вы можете достаточно просто и быстро настроить отправку событий вопределённый слак-канал, втом числе событий VelocityAlert. Это было почти то, что мы искали. Интеграция позволяла оперативно получать информацию отом, что всё плохо, но приэтом нельзя было фильтровать события поверсии. Витоге канал больше напоминал свалку.


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


Новая схема выглядела так:



Обрабатываем данные


Систочником данных вроде определились. Теперь нужно эти данные обрабатывать.


Напомню, что наша старая система наFabric делала сданными окрашах:


  1. Искала ответственного поGit-Blame.
  2. Создавала задачу наисправление.
  3. Оповещала оновом событии в специальный слак-канал.

Первое отчего мы решили отказаться это автоматическое создание задачи и поиск ответственного поGit-Blame. Поопыту, автоматически созданные задачи отправлялись накладбище Jira, и кним редко кто возвращался, а поиск поGit-Blame иногда давал сбой, что ещё больше повышало шансы забыть задачу. А вот оповещения вслак мы решили развивать, этот канал коммуникации показал себя наиболее эффективным.


Обработку решили реализовать набазе сервиса мобильных релизов Nupokati. Он собирает информацию поновым крашам, раз вдень покрону запрашивает дополнительные данные изBigQuery, фильтрует краши пофатальности и частоте возникновения нас не интересуют единичные сбои и отправляет daily report вслак поактуальной версии приложения.



Пример daily report


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


Помимо daily report мы отлавливаем VelocityAlert дляактуальной версии и тут же репортим опожаре вслак-канал и ответственному законкретный релиз инженеру. Втреде определяется, насколько взрыв фатален, и что сним делать.



Google Cloud Functions всё


Около года мы успешно эксплуатировали новую систему автоматического сбора и алертинга фатальных ошибок вмобильных приложениях. Уже практически забыли, как заходить вFirebase и смотреть краши. Как вдруг было объявлено, что интеграция Firebase-crashlytics и Google Cloud Functions deprecated и её работа будет приостановлена 1октября 2020года. Нужно было оперативно дорабатывать решение и отказываться отоблачных функций. Приэтом хотелось обойтись минимальными изменениями вработающей системе.



Так мы просто убрали Cloud Functions и доработали запрос наполучения данных изBigQuery. Вся остальная система осталась прежней: daily report, velocityAlerts, фильтры поколичеству задетых пользователей и слак-каналы. Новый запрос получает сразу все уникальные краши понужной версии и отправляет их впоток обработки.


SELECT issue_id, issue_title, is_fatal, COUNT(issue_id) as crashes_counter, ARRAY_AGG (distinct application.display_version) AS versions, COUNT(DISTINCT installation_uuid) AS affected_users FROM `android.firebase_crashlytics.{table}`WHERE is_fatal=true GROUP BY issue_title, issue_id, is_fatal HAVING ARRAY_LENGTH(versions)=1 AND "{version}" in UNNEST(versions)ORDER BY crashes_counter DESC

Итоги


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


Несколько советов тем, кто захочет повторить наш путь:


  • Использование BigQuery платное, но есть песочница, вкоторой можно поэкспериментировать.
  • Оптимизируйте запросы кBigQuery. Процессинг данных не бесплатный, он впрямом смысле имеет денежное выражение согласно тарифам.
  • Дляоптимизации затрат нахранение данных вBigQuery уменьшайте время жизни таблиц, это есть внастройках. Длянас оптимальным отказался период жизни таблицы впять дней.
  • Уже после создания нашей системы появился BigQuery streaming. Нанём можно собрать аналогичную систему или даже лучше.
  • Внимательней читайте документацию кGoogle Cloud Platform. Это очень мощная платформа смножеством инструментов и возможностей.
Источник: habr.com
К списку статей
Опубликовано: 15.09.2020 12:21:41
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Блог компании авито

Разработка мобильных приложений

Алерты

Мониторинг

Краш-репорт

Google cloud functions

Мобильные приложения

Категории

Последние комментарии

© 2006-2020, personeltest.ru