nuxt-error-tracker
v0.1.19
Published
[English](./README.md) · **Українська**
Readme
nuxt-error-tracker
English · Українська
Легкий трекер frontend-помилок для Nuxt 3 / 4. Перехоплює неопрацьовані помилки, невдалі запити та ланцюжок дій користувача (breadcrumbs), після чого відправляє їх на ваш власний Observer ingest-endpoint. Працює лише на клієнті, батчами, і збудований так, щоб ніколи не ламати застосунок-хост — якщо його не налаштовано, він не робить нічого.
Ви даєте URL і публічний ключ; пакет робить перехоплення. Усе відправлене потрапляє під ваш проєкт у вашому Observer-дашборді, де ви самі переглядаєте, шукаєте та керуєте логами.
Зміст
- Як це працює (складові)
- Встановлення
- Отримати endpoint і ключ
- Налаштування
- Конфігурація через змінні середовища
- Опції
- Runtime-хелпери
- Що відправляється на API
- Формат передачі та типи даних
- Перегляд і контроль ваших логів
- Приватність
- Ліцензія
Як це працює (складові)
Складових три. Цей пакет — лише перша.
| Складова | Що це | За що відповідає |
| --- | --- | --- |
| Цей пакет (nuxt-error-tracker) | Nuxt-модуль, який ви ставите у застосунок | Підключається до браузера, перехоплює помилки + breadcrumbs, батчить їх і POSTить на ваш endpoint. Працює лише на клієнті. |
| Ваш ingest-endpoint (.../v1/ingest) | Observer API, який ви хостите (або маєте доступ) | Автентифікує запит за публічним ключем + origin, валідує та rate-лімітує, дедуплікує помилки за fingerprint і зберігає. |
| Ваш дашборд | Консоль Observer | Де ви переглядаєте, шукаєте та керуєте логами — стек-трейси, occurrences, breadcrumb-ланцюжки, retention. |
Пакет ніколи не вирішує, куди йдуть дані — вирішуєте ви, вказавши
endpoint на свій хост. Дефолтного URL немає. Змініть хост — логи підуть за
ним. Це і є «bring your own backend»: той самий пакет працює проти localhost під
час розробки та проти вашого прод-Observer у релізі, лише зміною endpoint.
Усередині самого пакета:
module.ts— Nuxt-модуль. Читає ваші опції та реєструє плагін перехоплення лише колиenabled+endpoint+publicKeyприсутні всі. Немає будь-чого → плагін не додається, застосунок працює недоторканим. Також нормалізує опції (обрізаєmaxBatch ≤ 20,maxBreadcrumbs ≤ 50під серверні ліміти) і додає TypeScript-типи для хелперів.plugin.client.ts— власне рушій перехоплення (лише браузер). Слухає помилки, обгортаєfetch, патчитьconsole.error, пише breadcrumbs і керує чергою відправки.plugin.server.ts— маленька SSR-заглушка. Дає no-op$trackError/$addBreadcrumb, щоб виклик під час серверного рендеру ніколи не кидав помилку.ingest.codec.ts— саморобний protobuf-енкодер (без залежностей), що перетворює батч на бінарний формат, який очікує API.
Встановлення
npm i nuxt-error-tracker
# або: bun add nuxt-error-trackerОтримати endpoint і ключ
Модулю потрібні рівно дві речі, щоб почати звітувати: ingest-endpoint (ваш URL) і публічний ключ (якому проєкту належать помилки). Обидва беруться з вашого Observer-дашборда.
- Відкрийте дашборд за адресою
https://<ваш-observer-хост>/dashboard. - Створіть акаунт — введіть email і пароль (мін. 8 символів), надішліть. Ви лишаєтесь залогіненим через захищений httpOnly-cookie.
- Натисніть New project, дайте назву (напр.
my-app) і за бажанням перелічіть origins, на яких працює ваш сайт (через кому, напр.https://my-app.com, http://localhost:3000). Порожньо = приймати будь-який origin. Це серверний allowlist — запити з інших origins відхиляються. - Проєкт тепер показує публічний ключ (
pk_…) і готовий сніпетnuxt.config. Використайте Copy key / Copy config. - Endpoint — це ваш хост +
/v1/ingest, тобтоhttps://<ваш-observer-хост>/v1/ingest.
Що насправді роблять ці два значення
endpoint— куди відправляти. Це ваш URL. Вказуйте на будь-який хост, де крутиться Observer API. Хостованого дефолту немає; ви завжди шлете на свій бекенд.publicKey— чий проєкт помилки. На кожному запиті API шукає проєкт за цим ключем (заголовокX-Public-Keyабо query?key=для beacon-шляху), потім застосовує allowlist origins і rate-ліміт цього проєкту. Це публічне значення — безпечно класти у клієнтські бандли. Воно дає лише запис (ingest) в один проєкт; воно не дає нікому читати ваші логи. Читання вимагає логіну в дашборд.
Origins важливі. Якщо ви задали allowlist, він мусить містити кожен origin, з якого віддається застосунок — прод-домени та
http://localhost:3000для локальної розробки — інакше запити браузера отримають401. Лишіть порожнім під час експериментів, звузьте для продакшену.
Після налаштування (нижче) і деплою перехоплені помилки з'являються під цим проєктом у дашборді. Відкрийте Logs на проєкті, щоб їх переглянути.
Налаштування
Додайте модуль і вкажіть йому ingest-endpoint з публічним ключем вашого проєкту:
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['nuxt-error-tracker'],
errorTracker: {
endpoint: 'https://<ваш-observer-хост>/v1/ingest',
publicKey: 'pk_xxxxxxxxxxxxxxxxxxxxxxxx',
},
})Це мінімум. Усе решта має розумні дефолти.
Немає ключів? Не проблема
Якщо endpoint чи publicKey порожні (або enabled: false), плагін не
реєструється, і застосунок працює недоторканим. Тому модуль можна лишати
встановленим, підключаючи ключі по-середовищах — dev-збірки без ключа просто не
трекають. Ні падіння, ні шуму в консолі, ні мережевого виклику.
Конфігурація через змінні середовища
Кожну опцію можна задати зі змінних середовища замість хардкоду, за конвенцією
публічних env у Nuxt runtimeConfig:
NUXT_PUBLIC_ERROR_TRACKER_ENDPOINT=https://<ваш-observer-хост>/v1/ingest
NUXT_PUBLIC_ERROR_TRACKER_PUBLIC_KEY=pk_xxxxxxxxxxxxxxxxxxxxxxxx
NUXT_PUBLIC_ERROR_TRACKER_APP_VERSION=1.4.2Підключіть їх у nuxt.config.ts:
export default defineNuxtConfig({
modules: ['nuxt-error-tracker'],
runtimeConfig: {
public: {
errorTracker: {
endpoint: process.env.NUXT_PUBLIC_ERROR_TRACKER_ENDPOINT,
publicKey: process.env.NUXT_PUBLIC_ERROR_TRACKER_PUBLIC_KEY,
appVersion: process.env.NUXT_PUBLIC_ERROR_TRACKER_APP_VERSION,
},
},
},
})Це рекомендований спосіб націлити ту саму збірку на різний endpoint за середовищем (staging vs. production) без правки коду.
Опції
| Опція | Тип | Дефолт | Опис |
| --------------------- | --------- | ----------- | ------------------------------------------------------------------ |
| endpoint | string | '' | Ingest URL (.../v1/ingest). Обовʼязково — ваш хост. |
| publicKey | string | '' | Публічний ключ вашого проєкту (pk_…). Обовʼязково. |
| appVersion | string | 'unknown' | Тегує кожен батч релізом/версією для фільтрації. |
| enabled | boolean | true | Головний вимикач. false = повністю вимкнено, плагін не реєструється. |
| captureFetch | boolean | true | Обгортає fetch, щоб breadcrumbʼити запити і звітувати про збої / 5xx. |
| captureFetchDetails | boolean | true | Додає повну деталь запиту/відповіді (метод, статус, тайминг, заголовки, тіло запиту, тіло відповіді) до кожного fetch-breadcrumb. Чутливі заголовки редагуються, тіла обрізаються до 2 КБ. false — лишити fetch-крихти лише-повідомленням (не захоплювати payload / PII). |
| captureConsole | boolean | true | Патчить console.error, щоб ловити залоговані-але-не-кинуті помилки. |
| captureInputs | boolean | false | Записує значення полів форм як breadcrumbs (на change/blur). Чутливі поля маскуються. Opt-in — захоплює введені користувачем дані. |
| flushDelay | number | 2000 | Debounce (мс) перед відправкою черги-батча. |
| minFlushInterval | number | 0 | Мін. пауза (мс) між флашами — throttle частоти запитів під час шторму помилок. 0 = вимкнено. |
| sampleRate | number | 1 | Частка (0..1) захоплених помилок, що реально шлються; решта дропається при capture. 0.3 ≈ 30%. Ріже обʼєм до серверного rate limit. |
| maxPerMinute | number | 0 | Клієнтський ліміт помилок у чергу за rolling-хвилину (fixed window); понад — дроп. Дзеркалить серверний per-project rate limit. 0 = без ліміту. |
| maxPerSession | number | 5 | Макс. кількість occurrences тієї самої помилки за сесію. |
| maxBatch | number | 20 | Макс. помилок на запит (обрізається серверним лімітом 20). |
| maxBreadcrumbs | number | 30 | Breadcrumbs у ring buffer / на помилку (обрізається до 50). |
Runtime-хелпери
Плагін інʼєктить два хелпери для ручного використання:
const { $trackError, $addBreadcrumb } = useNuxtApp()
// Звітувати про опрацьовану помилку самому — опційний рівень серйозності
// ('fatal' | 'error' | 'warning' | 'info' | 'debug', дефолт 'error')
try {
await mayFail()
} catch (e) {
$trackError(e, 'warning')
}
// Додати власний breadcrumb у ланцюжок
$addBreadcrumb({
type: 'custom',
category: 'checkout',
message: 'user clicked pay',
level: 'info',
})Обидва хелпери завжди викликаються. Реальне перехоплення — лише на клієнті; на
сервері (SSR — напр. верхньорівневий <script setup>) це безпечні no-opʼи, тож
виклик ніколи не кидає помилку.
$trackError і $addBreadcrumb повністю типізовані — модуль постачає
augmentation для NuxtApp і Vue ComponentCustomProperties, тож і
useNuxtApp(), і використання в шаблоні бачать їх як функції (без unknown /
TS18046). Ручний .d.ts не потрібен.
Що відправляється на API
Нічого не відправляється, доки не станеться помилка. Пакет тримає в памʼяті ring buffer breadcrumbs, поки користувач взаємодіє; при перехопленні помилки він знімає снапшот цього ланцюжка, ставить помилку в чергу і (після debounce) POSTить батч.
Батч має дві частини: meta (раз на батч, застосовується до всіх помилок у
ньому) і errors (масив перехоплених помилок).
meta (раз на батч)
| Поле | Тип | Джерело | Нотатки |
| ------------ | -------- | ------- | ------- |
| device | object | navigator / window | userAgent, language, platform, screen (ШxВ), viewport (ШxВ), online (bool), memory (deviceMemory, ГБ або null), connection (effectiveType або null). Сервер обрізає JSON до 2 КБ. |
| userId | string | null | cookie user_id | Читається з cookie user_id, якщо є, інакше null. Пакет ніколи не вигадує id. Макс. 100 символів. |
| appVersion | string | ваша опція appVersion | Дефолт 'unknown'. Макс. 50 символів. |
errors[] (на кожну перехоплену помилку)
| Поле | Тип | Опис | Ліміт |
| ------------- | --------- | ---- | ----- |
| message | string | Повідомлення помилки. | ≤ 1000 симв. |
| stack | string | Стек-трейс. | ≤ 8000 симв. |
| source | string | Звідки: vue, window, promise, fetch чи console. | ≤ 20 симв. |
| level | string | Серйозність: fatal | error | warning | info | debug. | одне зі списку |
| url | string | window.location.href на момент перехоплення. | ≤ 2000 симв. |
| route | string | Nuxt-route fullPath. | ≤ 500 симв. |
| occurredAt | string | ISO-8601 timestamp. | ISO-8601 |
| breadcrumbs | Breadcrumb[] | Ланцюжок, що привів до помилки (нижче). | ≤ 50 елементів |
Кожен Breadcrumb:
| Поле | Тип | Опис | Ліміт |
| ----------- | -------- | ---- | ----- |
| type | string | click, input, navigation, fetch, console чи ваш кастомний тип. | ≤ 30 симв. |
| category | string | напр. ui.click, ui.input, navigation, http, console. | ≤ 60 симв. |
| message | string | Людиночитний рядок, напр. POST /api/pay → 500. | ≤ 500 симв. |
| level | string | info | error | … | ≤ 20 симв. |
| timestamp | string | ISO-8601. | ISO-8601 |
| data | object | Опційний структурований payload (деталі fetch: метод, статус, тайминг, заголовки, тіла запиту/відповіді). | JSON |
Ліміти батча: щонайбільше 20 помилок на запит; клієнт також повертає в
чергу невдалі батчі (обмежено 200 у памʼяті) і дедуплікує ту саму помилку до
maxPerSession (5) occurrences за сесію, тож одна повторювана помилка не
затопить вашу квоту.
Формат передачі та типи даних
Payloadʼи кодуються у protobuf (proto3) і POSTяться як
application/x-protobuf. Енкодер саморобний і без залежностей — жодного
protobufjs, тож споживачам не потрібні ні Vite optimizeDeps, ні
CJS-interop-конфіг.
Схема (номери полів визначальні — вони мусять збігатися з декодером API):
message Breadcrumb {
string type = 1;
string category = 2;
string message = 3;
string level = 4;
string timestamp = 5;
string data = 6; // обʼєкт у вигляді JSON-рядка
}
message Error {
string message = 1;
string stack = 2;
string source = 3;
string url = 4;
string route = 5;
string userId = 6; // per-error override (цим клієнтом не використовується)
string appVersion = 7; // per-error override (цим клієнтом не використовується)
string device = 8; // per-error override (цим клієнтом не використовується)
string occurredAt = 9;
repeated Breadcrumb breadcrumbs = 10;
string level = 11;
}
message Meta { string device = 1; string userId = 2; string appVersion = 3; }
message Batch { Meta meta = 1; repeated Error errors = 2; }device і breadcrumb data — обʼєкти, тож їдуть як JSON-рядки всередині
protobuf. Порожні рядки опускаються (дефолт proto3). Бінарне обрамлення лише
тримає мережеву панель охайною — це не шифрування; схема публічна, тож
будь-хто з нею може декодувати payload.
Як передається
- Звичайний шлях:
fetch(endpoint, { method: 'POST', headers: { 'Content-Type': 'application/x-protobuf', 'X-Public-Key': <ключ> }, body: <bytes>, keepalive: true }). - Шлях page-hide: черга примусово скидається через `navigator.sendBeacon(endpoint
- '?key=<ключ>', Blob)
— оскільки beacon не може ставити заголовки, ключ їде в query-параметрі?key=. Відкат доfetch(..., { keepalive: true }), якщоsendBeacon` недоступний чи відмовив.
- '?key=<ключ>', Blob)
API також приймає звичайний JSON на тому самому endpoint (та сама валідація), що зручно для скриптових тестових POSTів, але SDK завжди шле protobuf.
Перегляд і контроль ваших логів
Сенс власного endpoint: після відправки пакетом ви володієте даними і керуєте ними повністю зі свого дашборда — без сторонньої консолі.
- Перегляд — відкрийте Logs на проєкті, щоб побачити групи помилок, кожну
з повідомленням, лічильником, first/last seen, зачепленими routes і
стек-трейсом + breadcrumb-ланцюжком на кожну occurrence (включно з захопленими
fetch payload/response, коли
captureFetchDetailsувімкнено). - Дедуплікація — API групує occurrences за fingerprint (хеш нормалізованого повідомлення + верхні стек-фрейми), тож повторювана помилка — це один рядок із лічильником, а не тисячі дублікатів.
- Rate-ліміт і origins — кожен проєкт має власний per-minute rate-ліміт і allowlist origins. Звузьте origins, щоб відкидати сторонній трафік; самого ключа недостатньо, щоб дані зайшли з неперелічeного origin.
- Retention — occurrences видаляються після вікна retention (за замовчуванням 7 днів на сервері), тож сховище лишається обмеженим без жодних дій з вашого боку.
Оскільки ключ — лише на запис, а читання під логіном, публікація ключа у клієнтському бандлі не відкриває жодних збережених даних.
Приватність
Click-breadcrumbs записують елемент (tag / id / class / короткий текст / data-*
атрибути) — ніколи значення полів. Нічого не захоплюється, доки не станеться
помилка; breadcrumbs живуть лише в памʼяті і їдуть прикріпленими до помилки.
Значення полів захоплюються лише коли ви явно вмикаєте captureInputs: true.
Навіть тоді чутливі поля маскуються (зберігаються як [masked, N chars], ніколи
у відкритому вигляді): будь-який type="password", а також поля, чиї name /
id / autocomplete збігаються з pass, secret, token, otp, cvv,
card, iban, ssn, pin, auth тощо. Нечутливі значення обрізаються до 100
символів.
Захоплення деталей fetch (captureFetchDetails, увімкнено за замовчуванням)
редагує чутливі заголовки запиту/відповіді (authorization, cookie,
x-api-key, CSRF-токени, …) і обрізає тіла до 2 КБ. Поставте false, якщо не
хочете, щоб payloadʼи запитів чи тіла відповідей взагалі покидали браузер.
Ліцензія
PolyForm Shield 1.0.0 — source-available. Безкоштовно ставити й
використовувати для будь-якої мети, окрім створення продукту, що конкурує з
цим ПЗ або з тим, що ліцензіар надає з його допомогою. Не можна форкнути у
конкурента. Повні умови у LICENSE; авторські права лишаються за
автором.
