Перейти к содержимому

Автоматический перехват ​

Необработанные исключения и отклонённые промисы ловятся без всякой настройки — как только выполнился 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.