Вы выпустили релиз, QA всё проверил, метрики в первый день выглядят неплохо. Через неделю рейтинг в сторе падает с 4,6 до 4,2, а в отзывах появляется «вылетает при оплате». Знакомая ситуация? Без мониторинга ошибок Flutter-приложения вы узнаёте о проблеме последним.
Во Flutter проблема усугубляется тем, что приложение живёт сразу в нескольких слоях. Есть Dart-код, движок, нативные обвязки iOS и Android, плагины на Kotlin и Swift. Ошибка может возникнуть в любом из них, и стандартный лог в консоли разработчика здесь ничем не поможет.
На рынке два основных кандидата: Firebase Crashlytics и Sentry. Первый бесплатен и глубоко встроен в экосистему Google. Второй даёт более богатый контекст, трассировку производительности и гибкость в самостоятельном хостинге.
В статье разберём, чем грозят необработанные краши, что именно нужно собирать, как выглядит настройка обоих инструментов и по каким критериям выбирать. В конце соберём схему алертов, которая не превращается в шум и не даёт пропустить действительно важное.
Почему краши в продакшене стоят бизнесу пользователей и рейтинга
Мобильный пользователь нетерпелив. По открытым данным индустриальных отчётов, значительная часть людей удаляет приложение после одного-двух сбоев. Вернуть их потом стоит в разы дороже, чем удержать.
Второй удар приходится на сторы. Google Play учитывает crash rate и ANR rate в Android vitals: если доля сессий с крашами превышает порог около 1,09%, приложение хуже ранжируется в выдаче и может получить предупреждение в карточке. В App Store падение рейтинга сразу сказывается на конверсии из просмотра в установку.
Третий фактор — прямые деньги. Краш на экране оплаты или регистрации означает потерянную транзакцию. Если ошибка затрагивает 2% сессий на критичном шаге воронки, при 100 000 активных пользователей в месяц это тысячи неудачных попыток.
Ориентируйтесь на метрику crash-free users. Для зрелого продукта нормой считается 99,5% и выше, а значение ниже 99% — повод остановить разработку фич и заняться стабильностью.
Наконец, есть скрытая цена: время команды. Без репортов разработчик тратит часы на воспроизведение ошибки по обрывочному описанию из поддержки. С хорошим стек-трейсом и контекстом то же исправление занимает минуты.
Поэтому мониторинг — не «приятное дополнение перед релизом», а часть определения готовности. Его нужно подключать на первом спринте, а не после первых жалоб.
Что отслеживать: исключения Dart и нативные ошибки iOS и Android
Первое, что нужно понять: во Flutter существует несколько независимых каналов ошибок. Если подписаться только на один, часть крашей останется невидимой.
- FlutterError.onError — ошибки фреймворка, например исключения при построении виджетов или отрисовке.
- PlatformDispatcher.instance.onError — необработанные асинхронные исключения Dart, включая ошибки во Future без catch.
- Ошибки в изолятах — они не попадают в основной обработчик, нужен отдельный слушатель через Isolate.current.addErrorListener.
- Нативные краши — падения в Kotlin, Java, Swift, Objective-C и C++ (NDK), включая плагины.
- ANR и зависания — когда главный поток заблокирован дольше 5 секунд на Android.
Нативные краши особенно коварны. Приложение закрывается мгновенно, Dart-код не успевает ничего отправить, поэтому SDK должен перехватывать сигналы на уровне платформы и отправлять отчёт при следующем запуске.
Помимо самих падений, собирайте и некритичные ошибки: пойманные исключения сетевого слоя, ошибки парсинга JSON, неожиданные ответы API. Они не роняют приложение, но показывают деградацию задолго до жалоб.
Не отправляйте в репорты персональные данные: токены, email, номера карт. Настройте фильтрацию до отправки и проверьте, что тело запросов не попадает в breadcrumbs.
Для каждого отчёта важен минимальный контекст: версия и сборка приложения, модель устройства, версия ОС, состояние сети, идентификатор пользователя в обезличенном виде. Без этого приоритизировать ошибки почти невозможно.
Firebase Crashlytics: настройка, символикация и ограничения бесплатного плана
Если вы уже используете Firebase, Crashlytics во Flutter подключается быстро. Это самый короткий путь к первым краш-репортам мобильного приложения.
- Установите Firebase CLI и FlutterFire CLI, выполните flutterfire configure, чтобы сгенерировать firebase_options.dart.
- Добавьте пакеты firebase_core и firebase_crashlytics в pubspec.yaml.
- Инициализируйте Firebase в main() и перенаправьте ошибки: FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterFatalError.
- Для асинхронных ошибок задайте PlatformDispatcher.instance.onError, вызывая recordError с флагом fatal: true.
- Соберите релиз и принудительно вызовите тестовый краш через FirebaseCrashlytics.instance.crash().
Самая частая проблема — нечитаемые стек-трейсы. В релизной сборке Dart-код обфусцирован, а нативный нужно символизировать. Собирайте с флагами --obfuscate --split-debug-info=build/symbols и загружайте символы командой firebase crashlytics:symbols:upload. Для iOS добавьте скрипт загрузки dSYM в Xcode Build Phases, для Android подключите плагин Crashlytics Gradle с mapping-файлами.
Автоматизируйте загрузку символов в CI/CD. Забытые dSYM — причина №1, почему в консоли вместо методов видны адреса памяти.
Теперь об ограничениях. Сервис бесплатен без лимита на количество событий, и это его главный козырь. Но срок хранения данных составляет около 90 дней, гибкость фильтров скромная, а интеграции с трекерами и мессенджерами ограничены. Нет полноценной трассировки производительности внутри самого Crashlytics, для этого нужен отдельный Firebase Performance. Кроме того, данные хранятся в инфраструктуре Google, что может быть проблемой для проектов с жёсткими требованиями к локализации данных.
Sentry для Flutter: трассировка, breadcrumbs, релизы и сравнение с Crashlytics
Интеграция Sentry во Flutter строится вокруг пакета sentry_flutter. Он автоматически подключает обработчики Dart-ошибок, нативные SDK для iOS и Android и перехват ANR.
Базовая настройка укладывается в несколько строк: оборачиваете запуск приложения в SentryFlutter.init, указываете DSN, окружение и релиз. Для загрузки символов используйте sentry_dart_plugin, он отправляет debug-символы и source context прямо из CI.
Что выделяет Sentry:
- Breadcrumbs — цепочка событий перед ошибкой: навигация, тапы, HTTP-запросы, логи. Часто именно они объясняют причину краша.
- Трассировка производительности — транзакции и спаны для медленных экранов, запросов и холодного старта.
- Релизы и регрессии — привязка ошибок к версии, отслеживание новых проблем и состояния «исправлено в релизе X».
- Session Replay и скриншоты — визуальный контекст сбоя.
- Self-hosted вариант — полный контроль над данными.
Цена вопроса другая: у Sentry есть бесплатный тариф для небольших проектов, но при росте объёма событий платить придётся, а квоты нужно контролировать через sample rate и фильтры.
Для tracesSampleRate в продакшене начните с 0.1–0.2. Сто процентов трассировки быстро съедят квоту и добавят нагрузку на устройство.
Если сравнивать кратко: Crashlytics выигрывает ценой и простотой при уже подключённом Firebase, Sentry — глубиной контекста, единым инструментом для мобильного клиента и бэкенда и гибкостью алертов. Если у вас есть Flutter Web-версия, Sentry закроет и её тем же SDK, тогда как Crashlytics веб не поддерживает.
Как выбрать инструмент и настроить алерты для критичных ошибок
Выбор лучше делать по фактам, а не по привычке. Задайте себе четыре вопроса: нужен ли мониторинг веба и бэкенда в одном месте, есть ли требования к хранению данных, какой бюджет на инструменты и нужна ли трассировка производительности.
- Выбирайте Crashlytics, если проект небольшой или MVP, вы уже в Firebase, бюджет ограничен, а задача — просто видеть краши.
- Выбирайте Sentry, если важны breadcrumbs, релизы, производительность, веб-версия, интеграции с Jira, Slack, GitHub или self-hosted.
- Используйте оба, если хотите бесплатную базу в Crashlytics и детальную диагностику в Sentry. Но следите за дублированием обработчиков и нагрузкой.
Теперь алерты. Главная ошибка — слать всё подряд в общий канал. Через неделю его перестанут читать. Разделите уведомления по уровням критичности.
- Критичные: новая ошибка в релизе, рост crash-free users ниже 99%, краш на экранах оплаты и входа. Уведомление в Slack или Telegram и дежурному разработчику.
- Важные: регрессии уже закрытых проблем, ошибки, затрагивающие более 1% пользователей. Ежедневная сводка.
- Фоновые: редкие некритичные исключения. Разбор раз в спринт.
Назначайте владельца у каждой группы алертов. Уведомление без ответственного — просто шум, на который никто не реагирует.
Добавьте процесс: после каждого релиза 24–48 часов наблюдайте за дашбордом, используйте поэтапный rollout в сторах, а при росте крашей останавливайте выкатку. Если нужна помощь с архитектурой наблюдаемости, команда консалтинга по мобильным приложениям поможет выстроить процесс, а при разработке с нуля мы закладываем мониторинг в Flutter-приложения с первого спринта.
Ключевые выводы
- Подписывайтесь на все каналы ошибок: фреймворк, асинхронный Dart, изоляты и нативный слой.
- Crashlytics подходит для быстрого старта и экономии, Sentry — для глубокого контекста, трассировки и релизов.
- Автоматизируйте загрузку символов в CI, иначе стек-трейсы будут нечитаемыми.
- Делите алерты по критичности и назначайте ответственных, чтобы не тонуть в шуме.
Заключение
Мониторинг ошибок — это не выбор между «хорошим» и «плохим» инструментом, а подбор под зрелость продукта и команды. Crashlytics отлично закрывает базовую потребность: бесплатно, быстро, с нативной интеграцией в Firebase. Sentry раскрывается там, где нужно понять не только что сломалось, но и почему, на каком релизе и после каких действий пользователя.
Какой бы вариант вы ни выбрали, ключевые практики одинаковы. Подключите мониторинг до первого публичного релиза, настройте символикацию в CI, обрабатывайте все каналы ошибок и фильтруйте персональные данные. Затем выстройте простую систему алертов с понятными владельцами.
Начните с малого: сегодня проверьте, видите ли вы нативные краши и читаемы ли стек-трейсы в релизной сборке. Если да, переходите к настройке порогов и регламенту реакции. Если нет, это самый быстрый способ повысить стабильность и рейтинг приложения в ближайшем релизе.