Автоматический перехват
Необработанные исключения и отклонённые промисы ловятся без всякой настройки — как только выполнился init() (или загрузился скрипт с CDN), GlobalErrorIntegration уже слушает события окна error и unhandledrejection. Это единственная интеграция, включённая по умолчанию; всё остальное на этой странице подключается.
Перехват вручную
О пойманной ошибке сообщайте сами через captureException(). Хлебные крошки, добавленные до этого, уходят вместе с ней и показывают след событий, приведших к сбою:
ts
Buglapse.addBreadcrumb({ type: 'ui.click', level: 'info', message: 'Clicked "Pay" button' })
try {
await pay()
} catch (error) {
Buglapse.captureException(error)
}captureException() принимает только Error — если поймано что-то другое (строка, простой объект, брошенный библиотекой), сначала оберните: Buglapse.captureException(new Error(String(reason))).
Причины и составные ошибки
Цепочка cause ошибки уходит вместе с ней — до 10 причин, начиная с внешней, у каждой свой стек:
ts
try {
await loadCart()
} catch (error) {
Buglapse.captureException(new Error('Checkout could not start', { cause: error }))
}Страница инцидента показывает причины под ошибкой, а группировка учитывает их кадры. Причина, которая не Error ({ cause: 'timeout' }), показывается своим текстом. Члены AggregateError — то, чем отклоняется Promise.any(), — отправляются так же и показываются рядом с ошибкой; на группировку они не влияют.
Сообщения
captureMessage() сообщает о том, что заслуживает инцидента, но не является Error, — без стека, группируется по тексту:
ts
Buglapse.captureMessage('Payment provider returned an empty response', { level: 'warning' })level — debug, info (по умолчанию), warning, error или fatal; отправляется тегом level. Сообщение ниже error не делает сессию визита сессией с ошибкой. fingerprint работает так же, как у captureException().
Сообщение с переменными частями открывало бы инцидент на каждое значение. Запишите его через fmt — и оно будет группироваться по шаблону, Payment %s declined for %s:
ts
import { captureMessage, fmt } from '@buglapse/browser' // или Buglapse.fmt со скриптом с CDN
captureMessage(fmt`Payment ${payment.id} declined for ${user.email}`)Для строки, которая не должна становиться инцидентом, используйте логи.
Номер ошибки для пользователя
captureException() и captureMessage() возвращают id события — UUID, который можно показать в диалоге ошибки или приложить к обращению в поддержку:
ts
const eventId = Buglapse.captureException(error)
showError(`Что-то пошло не так. Номер: ${eventId}`)Вставьте его в поиск раздела Исключения как event_id:<id> (или передайте инструменту get-exception MCP), чтобы открыть именно это срабатывание. lastEventId() возвращает id последней отправленной ошибки или сообщения — для кода, который не ловил её сам, например error boundary, показывающего то, о чём сообщил глобальный обработчик. Id равен undefined, если событие отбросил beforeSend или интеграция, и если init() ещё не выполнялся (ошибка встаёт в очередь и отправляется после него).
Теги для одного перехвата
withScope() накладывает теги/контекст только на время своего колбэка, не трогая заданное глобально через setTag()/setContext():
ts
Buglapse.withScope((scope) => {
scope.setTag('checkout.step', 'payment')
scope.setContext('order', { id: order.id, total: order.total })
try {
chargeCard(order)
} catch (error) {
Buglapse.captureException(error)
}
})Наложение живёт только в синхронной части колбэка — await внутри теряет теги/контекст, потому что у браузерного клиента нет AsyncLocalStorage, чтобы перенести их. Ловите до await или заново задайте теги через setTag()/setContext() на самом клиенте, если перехват должен случиться после него. См. Теги и контекст.
Ошибки, которые не доходят до обработчика window
Некоторые виды сбоев вообще не проходят через слушатели error/unhandledrejection у GlobalErrorIntegration. Каждая интеграция ниже закрывает один из этих пробелов и, как любая встроенная, включается в Настройки → Скрипт загрузчика проекта, если подключение через CDN, — менять сниппет не нужно.
- BrowserApiErrorsIntegration —
throwвнутри колбэкаsetTimeout/setInterval, кадраrequestAnimationFrameили слушателя событий DOM/XHR из скрипта с другого домена доходит доwindowголымScript error.без объекта ошибки. Она оборачивает эти API и сохраняет исходную ошибку, так чтоGlobalErrorIntegrationсообщает о ней с полным стеком, а не отбрасывает. - ResourceErrorsIntegration — сломанные
<img>,<script>или<link>генерируют событиеerrorбезevent.error, которое не всплывает, так что слушательGlobalErrorIntegrationв фазе всплытия его пропускает. Она добавляет свой слушатель того же события в фазе перехвата. - HttpErrorsIntegration — неудачный HTTP-ответ (по умолчанию 5xx, настраивается
statusThreshold) или сетевой/CORS-сбойfetch/XHRвообще не бросает исключение JS. Она не вызываетcaptureException()— сообщает черезlog.error(), так что неудачные запросы видны в разделе Логи проекта, а не в Исключениях.
Своя группировка
Ошибки группируются в инциденты по шаблону отпечатка проекта. Если для каких-то ошибок это не то, что нужно, передайте свой отпечаток — '{{ default }}' в нём означает группировку проекта:
ts
// Один инцидент, где бы ни возникла ошибка.
Buglapse.captureException(error, { fingerprint: ['payment-gateway-down'] })
// Обычная группировка, разделённая по клиенту.
Buglapse.captureException(error, { fingerprint: ['{{ default }}', tenant.slug] })
// Всё, что будет поймано дальше; `null` возвращает группировку проекта.
Buglapse.setFingerprint(['{{ default }}', 'checkout'])scope.setFingerprint() делает то же внутри withScope(). Работает на тарифах со своей группировкой — см. Группировка из кода.
Меньше шума
Две интеграции сокращают отправляемое, обе — до того, как что-либо уйдёт в сеть:
- DedupeIntegration — отбрасывает исключение, если у него те же имя, сообщение и стек, что у пойманного непосредственно перед ним. Сравнение идёт только с последним исключением, а не со всей историей, так что та же ошибка, повторившаяся после другой, всё равно пройдёт.
- InboundFiltersIntegration — отбрасывает исключение по правилу до отправки:
ignoreErrorsсравнивается с сообщением (по умолчанию отсекает непрозрачный"Script error."браузера),denyUrls/allowUrls— с адресом верхнего кадра стека.
Обе работают на клиенте, до списания квоты, — отброшенное ими событие квоту проекта не расходует. Серверные фильтры, которые настраиваются без деплоя, описаны на странице Фильтрация шума.
Как пойманные исключения выглядят в панели
Каждое пойманное исключение попадает отдельной строкой в раздел Исключения проекта и также группируется в Инцидент — панель вычисляет отпечаток события (имя, сообщение, стек и любые другие поля, на которые ссылается шаблон отпечатка проекта) и сводит повторы одной ошибки в один инцидент со временем первого/последнего появления и счётчиком, а не заваливает список дубликатами.
Если задан release (через data-release или init({ release })), он проставляется в каждое исключение и используется, чтобы раскодировать минифицированный стектрейс в исходные файл/строку/колонку, когда загружены соответствующие source maps, — см. Source maps.