EUM мониторинг iOS-приложений
Какой способ подключения выбрать
Рекомендуемый способ — открытая библиотека dd-sdk-ios 3.x. Новые приложения подключайте по ней: описание начинается с раздела Введение.
iOS-агент Proto Observability (пакет protoobp-ios-sdk) — устаревший способ инструментации. Он по-прежнему поддерживается для приложений, которые уже его используют, и описан в конце страницы, в разделе iOS-агент Proto Observability (устаревший способ). Для таких приложений рекомендуется переход на dd-sdk-ios.
На этой странице:
- Введение
- Требования
- Какие модули подключать
- Адрес приёма данных
- Установка пакетов
- Инициализация
- Отслеживание экранов и действий
- Сетевые запросы и сквозная трассировка
- Приложения с WebView
- Пользовательские данные, события и ошибки
- Исходящий трафик приложения
- Проверка подключения
- Область применения инструкции
- Диагностика неполадок
- iOS-агент Proto Observability (устаревший способ)
Введение
Библиотека dd-sdk-ios (открытый исходный код, лицензия Apache 2.0) передаёт данные RUM в формате, который Proto Observability Platform принимает и обрабатывает штатно. Код библиотеки не меняется: всё, что нужно для передачи данных в платформу, задаётся параметрами инициализации.
Что при этом важно знать:
- Учётная запись Datadog не требуется. Токен клиента (
clientToken) и идентификатор приложения (applicationID) передаются в запросах как есть; платформа их не проверяет и не обращается к внешним сервисам для их валидации. Библиотека требует только, чтобы значения были непустыми. - По умолчанию библиотека отправляет данные в облако Datadog. Адрес приёма ProtoOBP нужно задать явно (
customEndpoint) в каждом подключённом модуле. Модуль безcustomEndpointпередаёт данные на серверы Datadog. - Адрес приёма подставляется целиком. Библиотека не дописывает путь к значению
customEndpoint, поэтому указывать нужно полный URL вместе с путём. - Синхронизация времени по умолчанию обращается к внешним NTP-серверам. Она отключается параметром
serverDateProvider, см. Исходящий трафик приложения.
Инструкция проверена на версии dd-sdk-ios 3.18.0. Для приложений, которые уже используют iOS-агент Proto Observability, см. раздел iOS-агент Proto Observability (устаревший способ) и Переход на dd-sdk-ios.
Состав атрибутов, которые собираются и становятся доступны в интерфейсе, приведён на странице Собираемые данные RUM и Mobile.
Требования
| Параметр | Значение |
|---|---|
| Версия библиотеки | 3.18.0 (проверена), ветка 3.x |
| Минимальная версия iOS | 15.0 |
| Xcode | 16 и новее |
| Менеджер пакетов | Swift Package Manager или CocoaPods |
| Сетевой доступ приложения | HTTPS к адресу приёма ProtoOBP с доверенным сертификатом (требование App Transport Security) |
Какие модули подключать
Библиотека состоит из независимых модулей. Для передачи данных RUM в платформу нужны два:
| Модуль | Подключать | Назначение и примечания |
|---|---|---|
DatadogCore | да | Ядро: хранение и отправка данных |
DatadogRUM | да | Экраны, действия пользователя, сетевые запросы, ошибки, длительные операции, время запуска |
DatadogTrace | по необходимости | Ручные спаны. Платформа принимает их с версии 204; лицензия должна включать модуль APM. Адрес задаётся явно: Trace.Configuration(customEndpoint: https://<адрес ProtoOBP>/api/v2/spans), иначе спаны уходят на серверы Datadog. Для сквозной трассировки сетевых запросов этот модуль не нужен |
DatadogLogs | нет | Логи приложения. Передача логов настраивается отдельно, см. Область применения |
DatadogCrashReporting | нет | Падения приложения. Платформа принимает их как ошибки RUM, но без символикации стектрейсов (загрузка dSYM не поддерживается) |
DatadogSessionReplay | нет | Запись сессий. Платформой не поддерживается |
DatadogWebViewTracking | по необходимости | Для приложений, часть экранов которых — веб-страницы в WKWebView: события веб-агента передаются в сессии приложения, см. Приложения с WebView |
DatadogProfiling | нельзя | Модуль обращается к серверам Datadog для проверки квоты, и этот адрес не переопределяется настройками |
DatadogFlags | нет | Функциональные флаги. Платформой не поддерживается; без собственных адресов обращается к серверам Datadog |
Модуль DatadogRUM не тянет за собой остальные: логи, падения, запись сессий и WebView не собираются, пока соответствующий модуль не подключён и не включён в коде.
Адрес приёма данных
Данные RUM принимаются по адресу:
https://<адрес ProtoOBP>/api/v2/rum
Равнозначный адрес, если на вашем периметре уже настроена маршрутизация по этому префиксу:
https://<адрес ProtoOBP>/mobile/api/v2/rum
Адрес указывается без косой черты в конце. Параметры запроса (всё, что после ?) библиотека заменяет своими, поэтому передавать через них метки или ключи нельзя.
Перед подключением уточните у администратора платформы:
- полный адрес приёма для вашего контура;
- что лицензия платформы включает модуль мониторинга цифрового опыта (DEM) — без него запросы отклоняются с кодом 403;
- что приём RUM включён в конфигурации компонента приёма данных.
Установка пакетов
Swift Package Manager. В Xcode: File → Add Package Dependencies, адрес https://github.com/DataDog/dd-sdk-ios, правило версии Exact Version 3.18.0. В цель приложения добавьте продукты DatadogCore и DatadogRUM.
Или в Package.swift:
dependencies: [
.package(url: "https://github.com/DataDog/dd-sdk-ios", exact: "3.18.0"),
],
targets: [
.target(
name: "MyApp",
dependencies: [
.product(name: "DatadogCore", package: "dd-sdk-ios"),
.product(name: "DatadogRUM", package: "dd-sdk-ios"),
]
),
]
При сборке Swift Package Manager загружает пакет целиком вместе с его зависимостями с github.com, даже если соответствующие модули не подключены: DataDog/dd-sdk-ios, kstenerud/KSCrash, open-telemetry/opentelemetry-swift-core, apple/swift-atomics. Если сборочный контур изолирован, эти репозитории нужно зеркалировать. CocoaPods загружает только подключённые модули.
CocoaPods. В Podfile:
pod 'DatadogCore', '3.18.0'
pod 'DatadogRUM', '3.18.0'
Инициализация
Библиотеку нужно инициализировать как можно раньше — в application(_:didFinishLaunchingWithOptions:) или в init() структуры App. Пример для приложения на UIKit:
import UIKit
import DatadogCore
import DatadogRUM
/// Синхронизация времени без обращения к внешним NTP-серверам:
/// используются системные часы устройства.
final class DeviceClockDateProvider: ServerDateProvider {
func synchronize(update: @escaping (TimeInterval) -> Void) {
update(0)
}
}
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
// Адрес ProtoOBP. Путь /api/v2/rum добавляется ниже явно:
// библиотека сама его не дописывает.
let protoObp = URL(string: "https://protoobp.example.ru")!
Datadog.initialize(
with: Datadog.Configuration(
// Произвольная непустая строка: платформа токен не проверяет.
clientToken: "protoobp",
// Окружение — попадает в данные как тег и доступно для фильтрации.
env: "prod",
// Название приложения в интерфейсе платформы.
// Если не задано, используется bundle identifier.
service: "my-ios-app",
// Без обращения к внешним NTP-серверам.
serverDateProvider: DeviceClockDateProvider()
),
trackingConsent: .granted
)
RUM.enable(
with: RUM.Configuration(
// Произвольный идентификатор приложения.
applicationID: "my-ios-app",
// 100 — собирать все сессии, 10 — 10 % сессий.
sessionSampleRate: 100,
// Автоматическое отслеживание экранов и нажатий UIKit.
uiKitViewsPredicate: DefaultUIKitRUMViewsPredicate(),
uiKitActionsPredicate: DefaultUIKitRUMActionsPredicate(),
// Сетевые запросы; к запросам на эти домены добавляются заголовки трассировки.
urlSessionTracking: .init(
firstPartyHostsTracing: .traceWithHeaders(
hostsWithHeaders: ["api.example.ru": [.tracecontext]],
sampleRate: 100
)
),
// Полный адрес приёма ProtoOBP.
customEndpoint: protoObp.appendingPathComponent("api/v2/rum"),
// Внутренняя телеметрия библиотеки платформой не используется — отключаем.
telemetrySampleRate: 0
)
)
return true
}
}
Для приложения на SwiftUI тот же код инициализации размещается в init() структуры, помеченной @main.
Данные о пользователе, собственные атрибуты, действия и ошибки передаются отдельными вызовами после инициализации, см. Пользовательские данные, события и ошибки.
Параметры, которые не нужно менять относительно значений по умолчанию:
| Параметр | Значение по умолчанию | Почему оставить |
|---|---|---|
Datadog.Configuration.site | .us1 | При заданных адресах приёма в сеть не попадает. От него зависит каталог локального хранилища, поэтому смена значения в уже выпущенном приложении оставит неотправленные данные в старом каталоге |
Datadog.Configuration.remoteConfiguration | nil | Включение приводит к запросам к серверам Datadog |
RUM.Configuration.trackWatchdogTerminations | false | Завершения приложения системой относятся к падениям, которые в этой схеме не собираются |
RUM.Configuration.appHangThreshold | nil | Зависания главного потока — то же |
RUM.Configuration.featureFlags | по умолчанию | Экспериментальные режимы (в частности, viewUpdates) меняют формат событий экрана и платформой не поддерживаются |
Отслеживание экранов и действий
В версии 3.x экраны и нажатия не отслеживаются, пока не задан предикат или не размечены экраны вручную.
UIKit. Предикаты DefaultUIKitRUMViewsPredicate() и DefaultUIKitRUMActionsPredicate() из примера выше превращают каждый UIViewController в экран, а нажатия на элементы управления — в действия. Имя экрана — имя класса контроллера. Чтобы задать собственные имена или исключить служебные контроллеры, реализуйте протокол UIKitRUMViewsPredicate.
SwiftUI. Два способа — выбирайте один:
Ручная разметка — предсказуемые имена экранов:
struct CatalogView: View { var body: some View { List { /* ... */ } .trackRUMView(name: "Catalog") } } Button("Купить") { checkout() } .trackRUMTapAction(name: "Купить")Автоматическое отслеживание — параметры
swiftUIViewsPredicate: DefaultSwiftUIRUMViewsPredicate()иswiftUIActionsPredicate: DefaultSwiftUIRUMActionsPredicate(isLegacyDetectionEnabled: true)вRUM.Configuration. На iOS 17 и старше автоматическое определение нажатий в SwiftUI менее надёжно; приisLegacyDetectionEnabled: falseнажатия собираются только на iOS 18 и новее.
Одновременное использование ручной разметки и автоматического предиката SwiftUI даёт в интерфейсе лишние экраны со служебными именами (AutoTracked_HostingController_Fallback, имена корневых представлений).
Сетевые запросы и сквозная трассировка
Параметр urlSessionTracking включает сбор запросов URLSession как событий ресурсов: адрес, метод, код ответа, длительность, размер. Отдельная настройка делегата сессии для этого не нужна.
Для детализации длительности запроса (DNS, установка соединения, TLS, первый байт, загрузка) дополнительно вызовите после RUM.enable метод enableDurationBreakdown, указав класс делегата ваших сессий:
final class AppURLSessionDelegate: NSObject, URLSessionDataDelegate {}
URLSessionInstrumentation.enableDurationBreakdown(
with: .init(delegateClass: AppURLSessionDelegate.self)
)
let session = URLSession(configuration: .default, delegate: AppURLSessionDelegate(), delegateQueue: nil)
К запросам на домены из firstPartyHostsTracing добавляются заголовки трассировки. Агенты Proto Observability на бэкенде принимают заголовки стандарта W3C Trace Context (traceparent, tracestate) без дополнительной настройки, поэтому рекомендуется тип заголовков .tracecontext. Значение по умолчанию (.trace(hosts:)) отправляет и traceparent, и заголовки x-datadog-* — такой вариант тоже работает.
Домены указываются без схемы (api.example.ru, а не https://api.example.ru), допускаются маски вида *.example.ru.
Если на серверах из списка установлены агенты Proto Observability, обработка запроса на бэкенде продолжает трейс, начатый на устройстве пользователя: сетевой запрос с мобильного устройства и его обработка на бэкенде объединяются в единую цепочку — трейс распределённой транзакции, начавшейся на устройстве. В трейсе бэкенда запрос приложения отображается отдельным спаном с данными события RUM (адрес, код ответа, длительность, сессия).
Объединение работает в платформе версии 204 и новее. Библиотека версии 3.x передаёт 128-битные идентификаторы трейсов; в более ранних версиях платформы трейс бэкенда записывается, но с сетевым запросом мобильного приложения не связывается.
Приложения с WebView
Раздел относится к гибридным приложениям: часть функций реализована нативно (вход, push-уведомления, отдельные экраны), а остальное — веб-приложение (PWA), открытое в WKWebView.
Нативная часть подключается по этой инструкции, веб-часть — EUM Browser агентом Proto Observability версии 7.2.0 и новее, как обычный сайт. Модуль DatadogWebViewTracking связывает их: внутри приложения веб-агент передаёт свои события не на адрес приёма, а нативной библиотеке, и та отправляет их вместе со своими. В результате:
- экраны, нажатия, запросы и ошибки веб-части попадают в ту же сессию, что и нативные экраны;
- данные веб-части передаются только тогда, когда сессия приложения попала в выборку (
sessionSampleRateвRUM.Configuration); собственная выборка веб-агента внутри приложения не действует; - код веб-приложения не нужно менять под приложение: та же инициализация в обычном браузере отправляет данные напрямую, а внутри приложения — через библиотеку.
Нативная часть
Добавьте в цель приложения продукт DatadogWebViewTracking (CocoaPods: pod 'DatadogWebViewTracking', '3.18.0') и включите отслеживание для каждого экземпляра WKWebView до загрузки страницы:
import WebKit
import DatadogWebViewTracking
let webView = WKWebView(frame: .zero, configuration: WKWebViewConfiguration())
// Домены веб-приложения; поддомены подходят автоматически.
WebViewTracking.enable(webView: webView, hosts: ["online.example.ru"])
webView.load(URLRequest(url: URL(string: "https://online.example.ru/")!))
При уничтожении экрана с WebView отключите отслеживание:
WebViewTracking.disable(webView: webView)
Экран-контейнер с WebView размечается как обычный нативный экран (например, .trackRUMView(name: "Онлайн-банк") или через предикат UIKit). Экраны внутри веб-приложения передаёт веб-агент.
Сетевые запросы веб-приложения собирает веб-агент: WKWebView выполняет их в отдельном процессе, и нативная библиотека их не видит. Поэтому запросы не задваиваются, а firstPartyHostsTracing в нативной части нужен только для запросов самого приложения.
Веб-часть
Подключите агент и инициализируйте его так же, как на сайте, см. Подключение файла JavaScript. Внутри приложения параметры proxy, clientToken, applicationId и sessionSampleRate не используются, но их можно оставить: в обычном браузере они нужны.
Дополнительно задайте в инициализации два параметра:
POBP_RUM.init({
// … параметры как на сайте …
service: "online-bank-web",
// Обязательно задавать явно, см. ниже.
defaultPrivacyLevel: "mask-user-input",
// Сквозная трассировка запросов веб-приложения к бэкенду.
allowedTracingUrls: [{ match: "https://online.example.ru/api", propagatorTypes: ["tracecontext"] }],
});
defaultPrivacyLevel. Если параметр не задан, внутри приложения агент берёт уровень приватности у нативной библиотеки. Без записи сессий она сообщает уровеньmask, и названия всех нажатий в веб-части приходят какMasked Element. Значениеmask-user-inputскрывает введённые пользователем данные, но сохраняет подписи кнопок и ссылок;allowне скрывает ничего.service. Рекомендуется задать веб-части собственное имя, отличное отserviceнативной части: так в событиях видно, где произошла ошибка или медленный запрос — в нативе или в веб-приложении.
Пользователь
Данные пользователя из нативной части в события веб-части не переносятся. Передайте один и тот же идентификатор с обеих сторон:
// Нативная часть, после входа
Datadog.setUserInfo(id: "1234")
// Веб-приложение, после получения данных о входе от нативной части
POBP_RUM.setUser({ id: "1234" });
При выходе пользователя сбросьте данные в обеих частях (Datadog.clearUserInfo() и POBP_RUM.clearUser()).
Как это выглядит в платформе
- Лента событий сессии показывает нативные и веб-события вместе, в порядке времени.
- Список сессий. Гибридная сессия отображается одной строкой, число экранов, действий и ошибок включает обе части. Сервис и тип источника у такой сессии берутся по одной из частей, поэтому фильтр по сервису или по типу «Веб»/«Мобильные» может её не показать. Для поиска конкретной сессии используйте поиск по пользователю или идентификатору сессии.
- Дашборды приложения считают сессию и в сервисе нативной части, и в сервисе веб-части. Число сессий по двум сервисам не складывайте: одна и та же сессия будет посчитана дважды.
- Сквозная трассировка. Запросы веб-приложения к бэкенду с агентами Proto Observability объединяются с трейсом бэкенда так же, как запросы с сайта.
Ограничения
- Отслеживается только
WKWebView. ВSFSafariViewControllerиASWebAuthenticationSessionприложение не может передать данные веб-агенту. - Страницы на доменах, которых нет в
hosts(например, страницы подтверждения платежа или входа через внешние сервисы), не отслеживаются. - Если веб-приложение использует Service Worker, в
Info.plistдолжен быть задан списокWKAppBoundDomains. В этом режиме библиотека может подключиться только к страницам доменов из этого списка — домен веб-приложения должен быть и вWKAppBoundDomains, и вhosts. - Привязка событий веб-части к конкретному нативному экрану-контейнеру передаётся библиотекой только вместе с записью сессий, которая платформой не поддерживается. Веб-события связаны с нативными через общую сессию.
- Запись сессий (
DatadogSessionReplay) не подключайте.
Пользовательские данные, события и ошибки
Кроме автоматически собираемых данных (экраны, нажатия, сетевые запросы) приложение может передавать собственные: кто пользователь, к какой организации он относится, в каком контексте работает, какие бизнес-события и ошибки происходят. Для этого используются штатные методы библиотеки, отдельная интеграция не требуется. Вызывайте их после Datadog.initialize и RUM.enable: вызовы до инициализации игнорируются и не передаются.
Сводка: что вызывать и где это видно в платформе.
| Что передаётся | Вызов | Поле события | Где видно в платформе |
|---|---|---|---|
| Пользователь | Datadog.setUserInfo(id:name:email:extraInfo:) | usr.id, usr.name, usr.email, usr.<поле> | Список и карточка сессии; число пользователей на дашбордах считается по usr.id; теги usr.* у всех событий |
| Организация (клиент B2B) | Datadog.setAccountInfo(id:name:extraInfo:) | account.id, account.name, account.<поле> | Теги account.* у всех событий |
| Атрибуты сессии | RUMMonitor.shared().addAttribute(forKey:value:) | context.<ключ> | Теги context.* у всех последующих событий |
| Атрибуты экрана | RUMMonitor.shared().addViewAttribute(forKey:value:) | context.<ключ> | Теги context.* у событий текущего экрана |
| Бизнес-событие | RUMMonitor.shared().addAction(type: .custom, name:attributes:) | action.target.name, context.<ключ> | Событие action: custom (<имя>) в трейсе сессии, поля — теги context.* |
| Ошибка | RUMMonitor.shared().addError(…) | error.type, error.message, error.stack, error.source | Ошибки мобильного приложения на дашборде, событие error: <тип> в трейсе сессии |
| Функциональный флаг | RUMMonitor.shared().addFeatureFlagEvaluation(name:value:) | feature_flags.<имя> | Теги feature_flags.* у событий экрана |
| Отметка времени на экране | RUMMonitor.shared().addTiming(name:) | view.custom_timings.<имя> | Метрика custom_<имя> у события экрана, в наносекундах от открытия экрана |
Все поля доступны для поиска и фильтрации событий, а через семантический слой — для собственных дашбордов и выборок. Вложенные значения разворачиваются в ключи через точку.
Данные пользователя
Вызывайте после входа пользователя в приложение и при смене пользователя. id обязателен, остальное — по необходимости. Произвольные поля в extraInfo попадают в то же поле usr рядом с id, name и email.
Datadog.setUserInfo(
id: "1234",
name: "Василий Пупкин",
email: "vass@pupkine.com",
extraInfo: ["tariff": "business_pro"]
)
// Дополнить уже заданные данные, не перезаписывая id, name и email:
Datadog.addUserExtraInfo(["segment": "corporate"])
// При выходе пользователя из приложения:
Datadog.clearUserInfo()
Данные пользователя добавляются ко всем событиям, отправленным после вызова, включая события текущего экрана. События, отправленные раньше, не меняются.
Даже без setUserInfo библиотека присваивает каждой установке приложения анонимный идентификатор usr.anonymous_id (параметр trackAnonymousUser в RUM.Configuration, по умолчанию включён). Он позволяет связать сессии одного устройства до входа пользователя. Число пользователей на дашбордах считается только по usr.id.
Данные организации
Если приложением пользуются сотрудники разных компаний-клиентов, организацию удобно передавать отдельно от пользователя:
Datadog.setAccountInfo(id: "acc-7", name: "ООО Ромашка", extraInfo: ["region": "msk"])
Datadog.addAccountExtraInfo(["plan": "enterprise"])
Datadog.clearAccountInfo()
Атрибуты
Атрибуты бывают трёх видов:
- атрибуты сессии добавляются ко всем последующим событиям: тип клиента, тарифный план, роль, признак участия в эксперименте;
- атрибуты экрана действуют только до ухода с текущего экрана: вариант оформления, источник перехода;
- атрибуты события передаются вместе с конкретным действием или ошибкой, как в примерах ниже.
let rum = RUMMonitor.shared()
// Атрибуты сессии
rum.addAttribute(forKey: "client_segment", value: "corporate")
rum.addAttributes(["tariff": "business_pro", "ab_group": "B"])
rum.removeAttribute(forKey: "ab_group")
// Атрибуты текущего экрана
rum.addViewAttribute(forKey: "screen_variant", value: "B")
Все три вида попадают в поле context события и доступны в платформе под ключами вида context.client_segment. Значение может быть строкой, числом, логическим значением или вложенной структурой (Encodable).
Пользовательские действия
Действие с типом custom — основной способ передать бизнес-событие вместе с произвольным набором полей:
RUMMonitor.shared().addAction(
type: .custom,
name: "payment_confirmed",
attributes: [
"payment_type": "p2p",
"amount": 12500,
"currency": "RUB"
]
)
Для действий с длительностью используются парные вызовы — платформа получит длительность действия:
RUMMonitor.shared().startAction(type: .custom, name: "document_upload")
// … загрузка …
RUMMonitor.shared().stopAction(type: .custom, name: "document_upload", attributes: ["pages": 3])
Действие, не остановленное через stopAction, завершается автоматически через 10 секунд.
Имя действия (payment_confirmed) становится значением поля action.target.name, в трейсе сессии событие называется action: custom (payment_confirmed). Поля из attributes попадают в context этого события.
Ошибки
Падения приложения в этой схеме не собираются (модуль DatadogCrashReporting не подключается), а ошибки, которые приложение обработало само, передаются явно. Библиотека фиксирует у каждой ошибки экран, сессию, пользователя и версию приложения, в контексте которых она произошла.
Если есть объект ошибки — тип и сообщение определяются по нему:
do {
try await transferService.send(request)
} catch {
RUMMonitor.shared().addError(error: error, source: .source, attributes: ["payment_type": "p2p"])
}
Если объекта нет — тип, сообщение и стектрейс задаются явно:
RUMMonitor.shared().addError(
message: "Не удалось списать средства",
type: "TransferFailed",
stack: Thread.callStackSymbols.joined(separator: "\n"),
source: .custom,
attributes: ["payment_type": "p2p", "amount": 12500]
)
Параметр source указывает происхождение ошибки: .source — ошибка в коде приложения, .network — сетевая, .custom — прочие. Тип (type) используется для группировки ошибок, поэтому его лучше делать стабильным и не включать в него переменные значения: номера заказов, идентификаторы.
Функциональные флаги
Если приложение использует функциональные флаги или A/B-эксперименты, передайте результат вычисления флага — события экрана можно будет сравнивать по вариантам:
RUMMonitor.shared().addFeatureFlagEvaluation(name: "new_checkout", value: true)
Собственные замеры времени
Отметка времени внутри экрана — например, момент, когда отрисован каталог или загрузились данные:
RUMMonitor.shared().addTiming(name: "catalog_rendered")
Значение — время от открытия экрана до вызова, в наносекундах. В платформе оно доступно как метрика custom_catalog_rendered события экрана. Имя должно быть уникальным в пределах экрана.
Персональные данные
Имя, email и произвольные поля пользователя хранятся в аналитическом контуре платформы вместе с остальными событиями RUM и доступны всем, у кого есть права на просмотр данных приложения. Передавайте только те персональные данные, которые необходимы для разбора обращений пользователей, и не помещайте в атрибуты платёжные и паспортные данные, пароли и токены. Если email или имя передавать нельзя, достаточно id — внутреннего идентификатора пользователя в вашей системе.
Исходящий трафик приложения
При настройке по этой инструкции приложение обращается по сети только к адресу приёма ProtoOBP.
| Источник трафика | Поведение по умолчанию | Настройка в этой инструкции |
|---|---|---|
| Отправка данных модулей | Серверы Datadog | customEndpoint с адресом ProtoOBP |
| Синхронизация времени | NTP-серверы 0…3.datadog.pool.ntp.org, UDP 123, при каждом запуске | serverDateProvider: DeviceClockDateProvider() |
| Внутренняя телеметрия библиотеки | Около 20 % сессий; отправляется на адрес приёма RUM, а не во внешние сервисы | telemetrySampleRate: 0 |
| Удалённая конфигурация | Выключена | Не включать |
| Проверка квоты профилирования | Только при подключённом DatadogProfiling | Модуль не подключать |
| Функциональные флаги | Только при подключённом DatadogFlags | Модуль не подключать |
Без serverDateProvider время событий корректируется по внешнему NTP. С системными часами устройства время событий берётся с устройства; явный сдвиг часов (события «из будущего» или отставание больше суток) платформа версии 204 и новее корректирует при приёме данных.
Проверка подключения
На время проверки включите подробный журнал библиотеки до вызова
Datadog.initialize:Datadog.verbosityLevel = .debugВ консоли Xcode появятся строки об отправке пакетов данных и кодах ответа.
Соберите и запустите приложение, пройдите по нескольким экранам, выполните сетевые запросы.
Подождите 1–2 минуты: данные передаются пачками, а не по каждому событию.
В интерфейсе платформы откройте раздел Мобильные приложения через меню. В списке появится приложение с названием, заданным в параметре
service.Перейдите на дашборд приложения. Заполняются просмотры экранов, сессии и пользователи, ошибки, сетевые запросы, разрезы по версиям приложения и операционным системам.
Перед выпуском приложения уберите Datadog.verbosityLevel = .debug.
Область применения инструкции
Инструкция описывает передачу данных RUM: просмотры экранов, действия пользователя, сетевые запросы, ошибки, длительные операции главного потока, время запуска приложения, а также пользовательские события и атрибуты.
В этой схеме подключения не передаются:
- падения приложения — модуль
DatadogCrashReportingне подключается; - логи приложения — передача логов настраивается отдельно, обратитесь к команде Proto Observability за параметрами для вашего контура;
- запись сессий (Session Replay);
- ручные спаны модуля
DatadogTraceв платформе до версии 204.
Диагностика неполадок
Данные не появляются, в журнале библиотеки ответ 200.
Проверьте, что в customEndpoint указан полный адрес вместе с путём /api/v2/rum и без косой черты в конце. Адрес https://<адрес>/mobile/ (с косой чертой) принимается, но данные не обрабатываются.
В журнале библиотеки адреса browser-intake-datadoghq.com или аналогичные.
У одного из подключённых модулей не задан customEndpoint, и его данные уходят на серверы Datadog. Задайте адрес или отключите модуль.
Ответ 403, тело Module not licensed.
Лицензия платформы не включает модуль мониторинга цифрового опыта (DEM). Обратитесь к администратору платформы.
Ответ 402, тело License expired.
Срок действия лицензии платформы истёк. Обратитесь к администратору платформы.
Ответ 404 на адрес /api/v2/spans.
Подключён модуль DatadogTrace, а платформа не принимает его спаны: приём добавлен в версии 204. Отключите модуль — для трассировки сетевых запросов он не нужен. В версии 204 и новее ответ 403 на этот адрес означает, что лицензия не включает модуль APM.
Запросы не уходят, в журнале ошибка App Transport Security. Адрес приёма должен работать по HTTPS с сертификатом, которому доверяет iOS. Для внутренних контуров с собственным удостоверяющим центром корневой сертификат устанавливается на устройства через профиль конфигурации.
В списке экранов имена вида AutoTracked_HostingController_Fallback.
Одновременно включены ручная разметка SwiftUI и автоматический предикат — оставьте один способ, см. Отслеживание экранов и действий.
Сетевой запрос в событиях RUM есть, а в трейсе бэкенда его нет.
Речь о спане запроса приложения в трейсе бэкенда. Проверьте по порядку: домен запроса указан в firstPartyHostsTracing без схемы; на сервере установлен агент Proto Observability; версия платформы — 204 или новее. Запрос не попадает в трейс и тогда, когда он не прошёл выборку: при sampleRate меньше 100 заголовки трассировки добавляются только к части запросов.
Экраны и нажатия не собираются, сетевые запросы есть. Не задан предикат отслеживания UIKit/SwiftUI и нет ручной разметки — в версии 3.x отслеживание экранов по умолчанию выключено.
В сессии нет событий веб-части, приложение с WebView.
Проверьте по порядку: WebViewTracking.enable вызывается до загрузки страницы; домен страницы указан в hosts; веб-агент версии 7.2.0 и новее загружается на странице. Проверить, что агент видит библиотеку, можно в инспекторе Safari (в отладочной сборке на iOS 16.4 и новее задайте webView.isInspectable = true): выражение !!window.DatadogEventBridge должно вернуть true.
Нажатия в веб-части приложения называются Masked Element.
В инициализации веб-агента не задан defaultPrivacyLevel, см. Веб-часть.
Приложение в списке называется как bundle identifier.
Название берётся из параметра service в Datadog.Configuration; если он не задан, используется bundle identifier.
iOS-агент Proto Observability (устаревший способ)
iOS-агент Proto Observability (POBP iOS SDK, пакет protoobp-ios-sdk) — устаревший способ инструментации iOS-приложений. Он по-прежнему поддерживается для приложений, которые уже его используют. Новые приложения подключайте через библиотеку dd-sdk-ios, как описано выше; для действующих приложений рекомендуется переход на dd-sdk-ios.
Подключение и инициализация агента
Для подключения агента используется SDK для автоматической инструментации (POBP iOS SDK).
Добавьте пакет protoobp-ios-sdk как зависимость, используя ваш менеджер пакетов:
| Менеджер пакетов | Способ подключения |
|---|---|
| CocoaPods | pod 'ProtoobpSDK', :git => 'https://git.proto.group/protoobp/protoobp-ios-sdk.git' |
| Swift Package Manager | .package(url: "https://git.proto.group/protoobp/protoobp-ios-sdk.git", .upToNextMajor(from: "2.0.0")) |
| Carthage | git "https://git.proto.group/protoobp/protoobp-ios-sdk.git" |
! Репозиторий с iOS SDK является приватным, учетную запись для скачивания пакетов можно получить у команды Proto.
Инициализируйте SDK и сделайте необходимые import:
import Protoobp //импортируйте пакет ProtoOBP SDK
// адрес для приема данных от RUM - данные должны проксироваться на https://proto-backend-server/mobile
let pobpEndpoint = URL(string: "https://your-site.ru/mobile")!
let pobpAppname = "ios-demo-app" // название приложения для отображения в интерфейсе
// список доменов для трейсинга - к запросам на эти домены будут добавляться заголовки для трейсинга
let firstPartyHosts: Set<String> = [
"https://my-site.ru",
"http://api.my-site.ru"
]
Protoobp.initialize(
appContext: .init(),
trackingConsent: .granted, // обязательно
configuration: Protoobp.Configuration
.builderUsing(
rumApplicationID: pobpAppname,
clientToken: "1", // обязательно 1
environment: "prod" // окружение, поддержка этого тега в интерфейсе появится позднее
)
.set(rumSessionsSamplingRate: 100.0) // 100.0 - все пользователи, 10.0 - 10% пользовательских сессиий
.set(sampleTelemetry: 0.0) // обязательно 0.0
.set(serviceName: pobpAppname)
.set(customTracesEndpoint: pobpEndpoint)
.set(customRUMEndpoint: pobpEndpoint)
.trackURLSession(firstPartyHosts: firstPartyHosts) //список доменов для трейсинга - к запросам на эти домены будут добавляться заголовки для трейсинга
.trackUIKitRUMViews() // обязательно
.trackUIKitRUMActions() // обязательно
.trackURLSession() // обязательно
.enableLogging(false) //обязательно
.build()
)
Global.sharedTracer = Tracer.initialize(configuration: .init()) // обязательно
// добавление данных о текущем пользователе - опционально
Protoobp.setUserInfo(
id: "1234", // строка, опционально
name: "Василий Пупкин", // строка, опционально
email: "vass@pupkine.com" // строка, опционально
)
Global.rum = RUMMonitor.initialize() // обязательно
Трейсинг запросов URLSession
Для сбора запросов отправленных от URLSession используйте POBPURLSessionDelegate как delegate для этой URLSession
let session = URLSession(
configuration: .default,
delegate: POBPURLSessionDelegate(),
delegateQueue: nil
)
Если уже используется кастомный delegate, есть два способа подключения:
- Используя наследование классов:
class YourCustomDelegateURLSessionDelegate: POBPURLSessionDelegate {
override func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
super.urlSession(session, task: task, didCompleteWithError: error) // отправка delegate на POBPURLSessionDelegate
/* код */
}
}
- Используя композицию классов:
private class YourCustomDelegateURLSessionDelegate: NSObject, URLSessionTaskDelegate, URLSessionDataDelegate, __URLSessionDelegateProviding {
// MARK: - __URLSessionDelegateProviding conformance
let pobpURLSessionDelegate = POBPURLSessionDelegate()
// MARK: - __URLSessionDelegateProviding handling
func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) {
pobpURLSessionDelegate.urlSession(session, task: task, didFinishCollecting: metrics) // отправка delegate на pobpURLSessionDelegate
/* код */
}
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
pobpURLSessionDelegate.urlSession(session, task: task, didCompleteWithError: error) // отправка delegate на pobpURLSessionDelegate
/* код */
}
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
pobpURLSessionDelegate.urlSession(session, dataTask: dataTask, didReceive: data) // отправка delegate на pobpURLSessionDelegate
/* код */
}
}
Сквозная трассировка запросов от iOS-приложения
Трейсы транзакции от iOS к инструментированным бэкендам, то есть бэкендам, на которых установлены агенты Proto OBP, будут объединяться в единую цепочку вызовов - трейс распределенной транзакции, начавшейся на iOS устройстве.

Переход на dd-sdk-ios
Данные iOS-агента Proto Observability и библиотеки dd-sdk-ios 3.x хранятся в одних и тех же таблицах и отображаются в одном разделе. Если задать service таким же, как в прежней конфигурации, история приложения продолжится без разрыва.
| iOS-агент Proto Observability | dd-sdk-ios 3.x |
|---|---|
import Protoobp | import DatadogCore, import DatadogRUM |
Protoobp.initialize(appContext:trackingConsent:configuration:) | Datadog.initialize(with:trackingConsent:) + RUM.enable(with:) |
.builderUsing(rumApplicationID:clientToken:environment:) | Datadog.Configuration(clientToken:env:service:), RUM.Configuration(applicationID:) |
.set(customRUMEndpoint: https://<адрес>/mobile) | customEndpoint: https://<адрес>/api/v2/rum — полный путь |
.set(sampleTelemetry: 0.0) | telemetrySampleRate: 0 |
.set(serviceName:) | Datadog.Configuration(service:) |
.trackUIKitRUMViews(), .trackUIKitRUMActions() | uiKitViewsPredicate: DefaultUIKitRUMViewsPredicate(), uiKitActionsPredicate: DefaultUIKitRUMActionsPredicate() |
.trackURLSession(firstPartyHosts:) + POBPURLSessionDelegate | urlSessionTracking: .init(firstPartyHostsTracing: …); делегат не обязателен |
Global.rum = RUMMonitor.initialize() | не нужно; монитор доступен как RUMMonitor.shared() |
Global.sharedTracer = Tracer.initialize(…) | не нужно для трассировки сетевых запросов |
Заголовки x-protoobp-* | traceparent (W3C) — агенты бэкенда принимают без настройки |