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

Обзор ​

breadcrumbsIntegration собирает след хлебных крошек за вас, так что каждое пойманное исключение приходит с записью того, что делали пользователь и приложение прямо перед ним. Она собирает пять независимых источников:

Источникtype крошкиЧто записывается
consoleconsoleКаждый вызов console.log/info/warn/error/debug с аргументами
dominteractionКлики и нажатия клавиш (keydown) на элементе, названном так, как его назвал бы человек, — button "Pay" in dialog "Confirm order", см. Как называется элемент. Какой символ напечатан, не записывается никогда; именованная клавиша (Enter, Escape, Tab, …) записывается в data.key
fetchfetchМетод, адрес и статус каждого запроса fetch() (уровень error при сбое или статусе не 2xx)
xhrxhrТо же для XMLHttpRequest
historynavigationПереходы в SPA (pushState и назад/вперёд) с адресами from и to

Без sendDefaultPii значения параметров в адресах записываются как [Filtered], а по тексту называется только элемент управления (кнопка, ссылка, вкладка, …), но не любой другой элемент — см. Текст и приватность и Чувствительные данные. Отдельные крошки отбрасывайте или переписывайте опцией beforeBreadcrumb в init().

Собственные запросы SDK на /api/envelope пропускаются, так что след они не засоряют. Как след хранится и показывается в панели — на странице Хлебные крошки.

Нажатия клавиш читаются из keydown (а не из устаревшего keypress, который не срабатывает для Escape). Автоповтор удерживаемой клавиши и одиночные модификаторы (Shift, Control, …) игнорируются.

Как называется элемент ​

Хлебная крошка взаимодействия говорит, на что нажали, одним коротким именем, собранным из того, что написали вы и видел пользователь, — и одинаковым в каждой сборке, так что одно и то же действие — один и тот же шаг во всех срабатываниях инцидента:

click on button "Pay" in dialog "Confirm order"

Текст и приватность ​

Прочитайте эту часть первой: имя может содержать текст вашей страницы.

  • Собственный текст элемента управления читается по умолчанию. Подпись кнопки, ссылки или вкладки — ваша, а не пользователя, и без неё у большинства кнопок не было бы имени вовсе.
  • Текст любого другого элемента (клик по абзацу, ячейке таблицы) читается только с sendDefaultPii.
  • Значение не читается никогда: ни у input, ни у select, ни у textarea, ни содержимое contenteditable, [role=textbox] или [role=combobox].
  • Каждая подпись очищается, откуда бы ни взялась: пробелы схлопываются, email заменяется на [Filtered], последовательность из четырёх и более цифр — на <num>, длина обрезается до 40 символов.

Остаётся элемент управления, текст которого принадлежит человеку, — ссылка «Иван Иванов» в списке клиентов, кнопка с названием аккаунта. Замаскируйте его:

html
<ul data-buglapse-mask>
    <li><a href="/customers/17">John Smith</a></li>
</ul>

Внутри data-buglapse-mask текст не читается, и элемент называется только по атрибутам (link, link "Customer" с aria-label). Классы интеграции записи по умолчанию, buglapse-mask и buglapse-block, делают то же самое: скрытое из записи скрыто и из следа. Если вы переименовали эти классы (maskTextClass, blockClass), используйте атрибут.

Какой элемент ​

Клик попадает в иконку кнопки или в span с её текстом. Крошка же — про элемент управления: ближайший из самого элемента и его предков, который является button, a[href], input, select, textarea, label, summary, contenteditable, элементом с интерактивной role (button, link, tab, menuitem, checkbox, radio, switch, option, combobox, textbox, …) или элементом с data-buglapse-name. Всё остальное остаётся собой. Поэтому клики по иконке и по тексту одной кнопки — одна серия и один rage click.

Имя ​

Берётся первое подходящее:

  1. data-buglapse-name — ваше собственное имя элемента, используется как есть:

    html
    <button data-buglapse-name="checkout.pay">…</button>

    → click on checkout.pay. Его ничто не переименует: ни редизайн, ни перевод.

  2. Роль и подпись — button "Pay", link "Cart", input "Email", checkbox "Remember me". Подпись — первое непустое из: aria-label; текст элементов, на которые указывает aria-labelledby; для поля — его <label>, затем placeholder, затем name (у кнопки-input подпись — её value); title; alt картинки внутри; собственный текст элемента.

  3. Test id — data-testid, иначе data-test: button[checkout-pay].

  4. {role}#{id}, иначе только роль: button#pay, div.

Роль — атрибут role элемента, если он есть, иначе по тегу: a → link; button и input-кнопки → button; input[type=checkbox|radio] → этот тип; input[type=range] → slider; любой другой input → input; всё остальное — имя тега. CSS-классы никогда не входят в имя.

Где на странице ​

Один уровень контекста различает две кнопки «Удалить» — ближайший предок, который является

  • элементом с data-buglapse-name → in cart;
  • dialog (или role="dialog" / "alertdialog") → in dialog "Confirm order" или in dialog без подписи;
  • form с aria-label, aria-labelledby, name или id → in form "login";
  • nav, header, footer, aside или соответствующей ролью-ориентиром → in nav, с её aria-label, если он есть.

Контейнер подписывается только по атрибутам, никогда по тексту.

В крошке ​

data.target — имя, из него же собирается сообщение. data.path — CSS-путь элемента (div.flex.gap-2 > button.px-4.py-2, до пяти уровней и 200 символов) — чтобы найти элемент в разметке; в имени он не участвует и свободно меняется между сборками.

Поведения ​

Сырой журнал событий не говорит, что произошло: пять кликов подряд — пользователь долбит кнопку, которая не отвечает, двадцать одинаковых запросов — цикл, а набор email съел бы половину следа из 50 крошек. Поэтому интеграция вливает повтор в предыдущую крошку, а начиная с порога называет серию в data.kind:

data.kindtypeКогдаСообщение
rage_clickinteraction3 и более клика по одному элементу, каждый в пределах 500 мс от предыдущегоrage click on {target}
inputinteractionподряд идущие символьные клавиши в одном input, textarea, select или contenteditabletyped in {target}
key_mashinteractionEnter или Escape 3 и более раз, каждый в пределах 500 мс от предыдущегоmashed {key} on {target}
request_loopfetch / xhrтот же метод, адрес и статус 5 и более раз, каждый в пределах 2 с от предыдущегобез изменений (GET /api/cart 200)
error_stormconsoleодно и то же сообщение console.error 5 и более раз подрядбез изменений
slow_interactioninteractionклик или нажатие клавиши, на которое страница отвечала дольше 500 мс (Event Timing API; пропускается, если браузер его не поддерживает)slow click on {target} / slow key press on {target}
reloadnavigationдокумент открыт перезагрузкойReloaded {url}

Каждая слитая крошка несёт data.count (сколько событий она представляет) и data.duration_ms (от первого до последнего из них) и сохраняет timestamp первого. Серия ниже порога тоже сливается и сохраняет простой вид: два быстрых клика — один click on {target} с count: 2, три одинаковых запроса — одна крошка с count: 3. Сообщение никогда не содержит счётчика, так что одинаковое поведение читается одинаково в каждом срабатывании.

Как различаются серии:

  • Клики объединяются, пока попадают в один элемент управления — его иконка и текст одно целое, а две кнопки, у которых просто одинаковое имя («Удалить» в двух строках списка), нет — и идут не реже чем раз в 500 мс. Уровень rage click — warn.
  • Набор текста не ограничен по времени: с паузами это по-прежнему одна крошка input, пока между нажатиями ничего не записано; Tab, Backspace, Delete и стрелки внутри считаются набором. Enter и Escape всегда начинают свою серию. Символьная клавиша вне редактируемого элемента (горячая клавиша) — крошка keydown, которая считается, но никогда не называется.
  • Запросы реже одного в 2 с (обычный опрос) не сливаются никогда. Уровень цикла запросов — warn или error, если запросы падали.
  • Вызовы консоли объединяются при одинаковых методе и сообщении; data.args — от первого вызова. error_storm становится только console.error.
  • Сливаются только соседние крошки. Всё, что записано между ними, заканчивает серию, так что цикл вперемешку с кликами виден как несколько коротких серий.
  • slow_interaction — отдельная крошка, которая добавляется после крошки клика (это становится известно только после отрисовки), с data.duration_ms и data.event (click или keydown).

Элементы, по которым кликают быстро намеренно ​

Клики по input[type=number], [role=spinbutton] и [role=slider] или внутри них считаются, но rage click не называются никогда. Для всего остального, по чему законно кликают сериями, — «+» у степпера, стрелка карусели — пометьте элемент или одного из его предков:

html
<button data-buglapse-rage="off">+</button>

Выключение ​

ts
use(breadcrumbsIntegration({ behaviors: false }))

записывает по крошке на событие: без слияния, без data.count, без названных видов, без крошек slow_interaction и reload. С подключением через CDN это переключатель Поведение в опциях интеграции в Настройки → Скрипт загрузчика.

Хлебная крошка путешествует с исключением, так что о поведении сообщается, только если за ним последовала ошибка, — кнопка, которая молча ничего не делает и ничего не бросает, ничего и не сообщит.

Использование ​

ts
import { use } from '@buglapse/browser'
import { breadcrumbsIntegration } from '@buglapse/browser/integrations'

use(breadcrumbsIntegration())

// или выключить отдельные источники:
use(breadcrumbsIntegration({ console: false, xhr: false }))

Опции ​

ОпцияТипПо умолчаниюОписание
consolebooleantrueЗаписывать вызовы консоли
dombooleantrueЗаписывать клики и нажатия клавиш
fetchbooleantrueЗаписывать запросы fetch()
xhrbooleantrueЗаписывать запросы XMLHttpRequest
historybooleantrueЗаписывать переходы в SPA
behaviorsbooleantrueСливать повторы в одну крошку и называть rage click, многократные нажатия клавиш, циклы запросов, шквалы ошибок, медленные взаимодействия и перезагрузки — см. Поведения

Крошки консоли и ConsoleIntegration можно включить вместе — одна добавляет крошку на каждый вызов, другая отправляет событие лога на каждый вызов.