EUM мониторинг Flutter-приложений
На этой странице:
- Введение
- Состав интеграции
- Адрес приёма данных
- Установка пакетов
- Инициализация
- Отслеживание экранов
- Сетевые запросы и сквозная трассировка
- Пользовательские события, атрибуты и ошибки
- Сетевые доступы
- Проверка подключения
- Область применения инструкции
- Диагностика неполадок
Введение
Пакет datadog_flutter_plugin передаёт данные RUM в формате, который Proto Observability Platform принимает и обрабатывает штатно. Чтобы данные приходили в платформу, достаточно указать в конфигурации пакета адрес приёма ProtoOBP.
Что при этом важно знать:
- Учётная запись Datadog не требуется. Токен клиента (
clientToken) и идентификатор приложения (applicationId) передаются в запросах как есть; платформа их не проверяет и не обращается к внешним сервисам для их валидации. Задайте любые непустые значения — они удобны как метки, но на приём данных не влияют. - Адрес приёма подставляется целиком. Пакет не дописывает путь к значению
customEndpoint, поэтому указывать нужно полный URL вместе с путём. Это самая частая причина, по которой данные не появляются. - Параметр
siteобязателен по контракту пакета, но при заданномcustomEndpointв отправке данных не участвует. Оставьте любое значение.
Для нативных мобильных приложений используются агенты Proto Observability: Android и iOS.
Состав атрибутов, которые собираются и становятся доступны в интерфейсе, приведён на странице Собираемые данные RUM и Mobile — Flutter-приложения используют ту же схему событий, что Android и iOS.
Состав интеграции
Подключается один пакет — datadog_flutter_plugin. Он покрывает обе мобильные платформы: приведённая ниже настройка работает и на Android, и на iOS, платформенно-зависимый код писать не нужно.
Отдельно подключать библиотеки для Android и iOS не требуется. Пакет представляет собой обвязку над платформенными библиотеками, и они подтягиваются как транзитивные зависимости при обычной сборке проекта:
- на Android —
dd-sdk-android-rum,dd-sdk-android-logs,dd-sdk-android-ndkверсии 3.11.0 через Gradle; - на iOS —
DatadogCore,DatadogRUM,DatadogLogs,DatadogCrashReporting,DatadogInternalверсии 3.13.0 через CocoaPods.
Версии платформенных библиотек зафиксированы в самом пакете. Ручные правки в build.gradle и Podfile не нужны, код инициализации на Kotlin и Swift писать не нужно — вся настройка выполняется на Dart.
Требования к среде для datadog_flutter_plugin 3.4.1: Flutter 3.27 и новее, Dart 3.6 и новее, Android minSdkVersion 23, iOS 12.0 и новее.
Пакет поддерживает и Flutter Web — в этом случае под капотом используется браузерная библиотека. Подключение веб-приложений, написанных не на Flutter, описано на странице Browser.
Адрес приёма данных
Данные RUM принимаются по адресу:
https://<адрес ProtoOBP>/api/v2/rum
Равнозначные адреса, если на вашем периметре уже настроена маршрутизация по одному из этих префиксов:
https://<адрес ProtoOBP>/eum/api/v2/rum
https://<адрес ProtoOBP>/mobile/api/v2/rum
Перед подключением уточните у администратора платформы:
- полный адрес приёма для вашего контура;
- что лицензия платформы включает модуль мониторинга цифрового опыта (DEM) — без него запросы отклоняются с кодом 403;
- что приём RUM включён в конфигурации компонента приёма данных.
Установка пакетов
flutter pub add datadog_flutter_plugin datadog_tracking_http_client
Инициализация
В lib/main.dart:
import 'package:datadog_flutter_plugin/datadog_flutter_plugin.dart';
import 'package:datadog_tracking_http_client/datadog_tracking_http_client.dart';
import 'package:flutter/material.dart';
// Адрес приёма ProtoOBP. Указывается ПОЛНОСТЬЮ, вместе с путём:
// пакет не дописывает /api/v2/rum к этому значению.
const protoObpRumEndpoint = 'https://protoobp.example.ru/api/v2/rum';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
final configuration = DatadogConfiguration(
// Произвольная непустая строка: платформа токен не проверяет.
clientToken: 'protoobp',
// Окружение — попадает в данные как тег и доступно для фильтрации.
env: 'prod',
// Обязателен по контракту пакета; при заданном customEndpoint не используется.
site: DatadogSite.eu1,
// Название приложения в интерфейсе платформы.
service: 'my-mobile-app',
nativeCrashReportEnabled: true,
// Домены вашего бэкенда — к запросам к ним добавляются заголовки трассировки.
firstPartyHosts: ['api.example.ru'],
rumConfiguration: DatadogRumConfiguration(
// Произвольный идентификатор приложения.
applicationId: 'my-mobile-app',
// 100 — собирать все сессии, 10 — 10 % сессий.
sessionSamplingRate: 100,
// Внутренняя телеметрия пакета платформой не используется — отключаем.
telemetrySampleRate: 0,
detectLongTasks: true,
// Метрики отрисовки кадров Flutter.
reportFlutterPerformance: true,
customEndpoint: protoObpRumEndpoint,
),
)..enableHttpTracking();
await DatadogSdk.runApp(configuration, TrackingConsent.granted, () async {
runApp(const MyApp());
});
}
Данные о текущем пользователе задаются отдельно и не обязательны:
DatadogSdk.instance.setUserInfo(
id: '1234',
name: 'Василий Пупкин',
email: 'vass@pupkine.com',
);
Отслеживание экранов
Чтобы переходы между экранами становились отдельными просмотрами, добавьте наблюдатель навигации:
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
navigatorObservers: [
DatadogNavigationObserver(datadogSdk: DatadogSdk.instance),
],
routes: {
'/': (_) => const HomeScreen(),
'/details': (_) => const DetailsScreen(),
},
);
}
}
Имя маршрута становится именем экрана в интерфейсе платформы, поэтому маршруты стоит называть осмысленно.
Сетевые запросы и сквозная трассировка
Вызов ..enableHttpTracking() в примере выше подменяет HttpOverrides.global, после чего сетевые запросы приложения собираются как события ресурсов. Если в приложении используются собственные HttpOverrides, установите их до инициализации.
К запросам на домены из firstPartyHosts добавляются заголовки трассировки. Если на этих серверах установлены агенты Proto Observability, вызовы с мобильного устройства и обработка на бэкенде объединяются в единую цепочку — трейс распределённой транзакции, начавшейся на устройстве пользователя.
Пользовательские события, атрибуты и ошибки
Кроме автоматически собираемых данных (экраны, нажатия, сетевые запросы, падения) приложение может отправлять собственные события — продуктовые и бизнесовые. Отдельный SDK для этого не требуется: используются штатные методы уже подключённого пакета, а платформа принимает такие события тем же потоком, что и остальные данные RUM.
Пользовательские действия
Событие с типом custom — основной способ передать продуктовое событие вместе с произвольным набором полей.
DatadogSdk.instance.rum?.addAction(
RumActionType.custom,
'payment_confirmed',
{
'payment_type': 'p2p',
'amount': 12500,
'currency': 'KGS',
'channel': 'mobile',
},
);
Для действий с длительностью используются парные вызовы startAction и stopAction — платформа получит длительность действия.
Имя события (payment_confirmed) становится значением поля цели действия, тип — значением custom. Оба поля доступны как измерения в дашбордах и выборках.
Атрибуты
Атрибуты бывают двух видов.
Разовые — передаются вместе с конкретным событием, как в примере выше.
Постоянные — задаются один раз и добавляются ко всем последующим событиям сессии. Удобны для сегментации: тип клиента, тарифный план, роль, признак участия в эксперименте.
DatadogSdk.instance.rum?.addAttribute('client_segment', 'corporate');
DatadogSdk.instance.rum?.addAttribute('tariff', 'business_pro');
Атрибуты попадают в поле context события. В платформе они доступны под ключами вида context.client_segment, context.amount — включая вложенные структуры, которые разворачиваются в ключи через точку.
Персональные данные в атрибуты помещать не следует: события хранятся в аналитическом контуре платформы и доступны всем, у кого есть права на просмотр данных приложения.
Ошибки и стектрейсы
Механизм фиксирования стектрейсов включён в пакет и дополнительной разработки не требует. Собираются три категории:
- Падения приложения — при
nativeCrashReportEnabled: true. Отправляются при следующем запуске приложения. - Необработанные исключения — перехватываются автоматически после инициализации.
- Ошибки, переданные явно — там, где приложение обрабатывает исключение само, но событие нужно зафиксировать.
try {
await transferService.send(request);
} catch (error, stackTrace) {
DatadogSdk.instance.rum?.addError(
error,
RumErrorSource.source,
stackTrace: stackTrace,
errorType: 'TransferFailed',
attributes: {'payment_type': 'p2p'},
);
rethrow;
}
Если исключения как объекта нет, а есть только сообщение, используется addErrorInfo(message, source, stackTrace: …).
Платформа сохраняет по каждой ошибке сообщение, тип, источник, полный стектрейс, признак падения приложения и отпечаток ошибки для группировки, а также экран, сессию, пользователя и версию приложения, в контексте которых ошибка произошла.
Что с этими данными можно делать в платформе
Пользовательские события и ошибки сохраняются двумя способами: как события RUM в исходном виде — со всеми переданными полями без потерь — и как элементы трейсов, где каждое поле события становится отдельным атрибутом для поиска и фильтрации.
Отсюда следует, что доступно:
- разрезы по типу и названию действия, по экрану, версии приложения, операционной системе, географии — на дашбордах мобильных приложений;
- построение собственных дашбордов и выборок по любому переданному атрибуту через семантический слой платформы;
- поиск конкретных событий и ошибок с переходом к сессии пользователя и предшествующим действиям, см. Трейсы сессий;
- воронки по последовательности пользовательских действий.
Обфусцированные стектрейсы
Восстановление обфусцированных стектрейсов на стороне платформы не выполняется — загрузка mapping-файлов R8/ProGuard и файлов dSYM не предусмотрена. Стектрейс сохраняется в том виде, в каком его сформировало приложение.
Практические варианты:
- отключить обфускацию для пакетов, ошибки в которых важно читать напрямую;
- восстанавливать стектрейсы своим mapping-файлом от соответствующей сборки вне платформы — для этого версия приложения фиксируется в каждом событии;
- передавать в атрибутах ошибки заранее подготовленное читаемое описание в дополнение к стектрейсу.
Сетевые доступы
Приложению для передачи данных достаточно доступа к адресу приёма ProtoOBP по HTTPS.
Дополнительно платформенные библиотеки при запуске выполняют синхронизацию времени по NTP с публичным пулом серверов. В контурах без доступа к внешним NTP-серверам синхронизация не выполняется, и используются системные часы устройства; на передачу данных это не влияет. Адреса NTP-серверов заданы в библиотеках и не выносятся в конфигурацию приложения — при жёстких требованиях к исходящему трафику это учитывается на уровне сетевой политики.
Проверка подключения
- Соберите и запустите приложение, пройдите по нескольким экранам, выполните сетевые запросы.
- Подождите 1–2 минуты: данные передаются пачками, а не по каждому событию.
- В интерфейсе платформы откройте раздел с мобильными приложениями. В списке появится приложение с названием, заданным в параметре
service. - Перейдите на дашборд приложения. Заполняются запуски, просмотры экранов, сессии и пользователи, ошибки, сетевые запросы, разрезы по версиям приложения и операционным системам.
Проверку следует выполнить на обеих платформах: сборки для Android и iOS используют разные платформенные библиотеки, хотя настраиваются одинаково.
Область применения инструкции
Инструкция описывает передачу данных RUM: просмотры экранов, действия пользователя, сетевые запросы, ошибки и падения со стектрейсами, метрики отрисовки, а также пользовательские события и атрибуты.
Передача логов приложения настраивается отдельно — обратитесь к команде Proto Observability за параметрами для вашего контура.
Запись сессий (Session Replay) в этой схеме подключения не передаётся.
Диагностика неполадок
Данные не появляются, в логах приложения нет ошибок отправки.
Проверьте, что в customEndpoint указан полный адрес вместе с путём /api/v2/rum. Если задан только адрес сервера, запросы уходят в корень сайта и до компонента приёма не доходят.
Данные приходят с Android, но не с iOS (или наоборот).
Настройка общая, но сборки разные. Проверьте, что на iOS выполнен pod install после установки пакета, а на Android значение minSdkVersion не ниже 23.
Ответ 403, тело Module not licensed.
Лицензия платформы не включает модуль мониторинга цифрового опыта (DEM). Обратитесь к администратору платформы.
Ответ 402, тело License expired.
Срок действия лицензии платформы истёк. Обратитесь к администратору платформы.
Ответ 415.
Запрос отправлен с типом содержимого, который компонент приёма не разбирает. Такое возможно при отправке через промежуточный прокси, изменяющий заголовки. Убедитесь, что прокси передаёт заголовки Content-Type и Content-Encoding без изменений.
Ответ 200, но данных в интерфейсе нет. Приём RUM может быть выключен в конфигурации компонента приёма данных. Обратитесь к администратору платформы — для диагностики на стороне платформы включается подробное протоколирование принимаемых пакетов.
Приложение в списке называется не так, как ожидалось.
Название берётся из параметра service конфигурации пакета.