Уведомления об ошибках фронтенда
Notifly прекрасно подходит как «дешёвая Sentry» для маленьких сайтов и pet-проектов:
ловите window.onerror, unhandledrejection, React error boundary — и шлите
краткое сообщение со стектрейсом и URL-ом.
Рекомендуемый способ: WebScript console_errors
Заголовок раздела «Рекомендуемый способ: WebScript console_errors»Что делает скрипт:
- Перехватывает
window.onerror,unhandledrejection,console.error. - Дедупликация в рамках сессии — повторные ошибки не шлются.
- Батчинг: до 10 ошибок в одном запросе, flush каждые 3 секунды.
- Использует
navigator.sendBeacon— не блокирует закрытие страницы. - Лимит 100 запросов за сессию — защита от бесконечных циклов.
- Весь батч = 1 событие квоты = 1 push-уведомление.
Юзкейсы
Заголовок раздела «Юзкейсы»| Сценарий | Почему Notifly вместо Sentry |
|---|---|
| Pet-проект на проде | Бесплатно, без лимитов на events, нет привязки к платформе |
| Лендинг на статике | Одна строка в <head> — никакого бэкенда не нужно |
| Внутренний инструмент | Достаточно знать «что упало» — без replay/tracing |
| Вайбкодинг с ИИ | ИИ-ассистенту легко вставить скрипт в любой фреймворк (см. LLM-инструкцию в админке) |
| Стейджинг / QA | Push сразу на телефон тестировщика при падении |
| E-commerce checkout | Узнать что юзеры видят ошибку на оплате раньше, чем они напишут в поддержку |
| Мобильный WebView | Один и тот же скрипт работает в Capacitor/Cordova/React Native WebView |
| Electron-приложение | В renderer HTML — без отдельного Sentry SDK |
Быстрый старт
Заголовок раздела «Быстрый старт»- Откройте app.notifly.ru/web-scripts.
- Нажмите Создать скрипт → тип Ошибки консоли.
- Скопируйте сниппет из вкладки «HTML вставка».
- Вставьте в
<head>вашего сайта до основных бандлов. - Готово — ошибки начнут приходить в push.
Альтернативный способ: собственный бэкенд-прокси
Заголовок раздела «Альтернативный способ: собственный бэкенд-прокси»Если вам нужен полный контроль (rate limiting, обогащение данными, source maps) — используйте тонкий прокси.
::: caution[Не светите токен в браузере]
Никогда не вставляйте app-токен прямо в JavaScript на странице. Вместо этого
сделайте тонкий прокси на бэкенде (POST /api/error-report → Notifly).
Иначе любой посетитель сможет спамить вашим Notifly.
:::
Прокси на бэкенде
Заголовок раздела «Прокси на бэкенде»Минимальный пример на Express (~15 строк):
const express = require('express');const fetch = require('node-fetch');const app = express();app.use(express.json({limit: '20kb'}));
const RATE = new Map(); // ip → timestampconst COOLDOWN = 30_000;
app.post('/api/error-report', async (req, res) => { const ip = req.ip; if (Date.now() - (RATE.get(ip) || 0) < COOLDOWN) { return res.status(204).end(); // молча глотаем спам } RATE.set(ip, Date.now());
const {title, message} = req.body || {}; if (!title) return res.status(400).end();
await fetch(`${process.env.NOTIFLY_URL}/message?token=${process.env.NOTIFLY_TOKEN}`, { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ title: `🌐 [front] ${title}`.slice(0, 200), message: String(message || '').slice(0, 1500), priority: 6, }), }); res.status(204).end();});
app.listen(3000);Vanilla JS трэкер
Заголовок раздела «Vanilla JS трэкер»Подключите на любую страницу:
<script>(function () { var ENDPOINT = '/api/error-report'; var lastSent = 0;
function send(payload) { if (Date.now() - lastSent < 5000) return; // не чаще 1 раза в 5с с одного клиента lastSent = Date.now(); try { fetch(ENDPOINT, { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(payload), keepalive: true, }); } catch (e) {} }
window.addEventListener('error', function (e) { send({ title: (e.message || 'unknown error').slice(0, 100), message: [ 'URL: ' + location.href, 'UA: ' + navigator.userAgent, 'At: ' + (e.filename || '?') + ':' + (e.lineno || '?') + ':' + (e.colno || '?'), '', (e.error && e.error.stack) ? e.error.stack : '' ].join('\n'), }); });
window.addEventListener('unhandledrejection', function (e) { var reason = e.reason || {}; send({ title: 'unhandledrejection: ' + (reason.message || String(reason)).slice(0, 80), message: 'URL: ' + location.href + '\n\n' + (reason.stack || ''), }); });})();</script>React ErrorBoundary
Заголовок раздела «React ErrorBoundary»import React from 'react';
export class ErrorBoundary extends React.Component { state = {error: null}; static getDerivedStateFromError(error) { return {error}; }
componentDidCatch(error, info) { fetch('/api/error-report', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ title: `React: ${error.name}: ${error.message}`.slice(0, 100), message: `URL: ${location.href}\n` + `UA: ${navigator.userAgent}\n\n` + (error.stack || '') + '\n\nComponent stack:' + (info?.componentStack || ''), }), keepalive: true, }).catch(() => {}); }
render() { if (this.state.error) { return <div className="err">Что-то пошло не так. Мы уже знаем.</div>; } return this.props.children; }}Используем:
import {ErrorBoundary} from './ErrorBoundary';
createRoot(document.getElementById('root')).render( <ErrorBoundary> <App /> </ErrorBoundary>);Что ещё можно ловить
Заголовок раздела «Что ещё можно ловить»- Слишком долгая загрузка:
PerformanceObserverдляlargest-contentful-paint, если LCP > 5с — отправлять предупреждение. - Сломанные изображения:
img.onerror→ массовый репорт пачкой. - Пользовательские «жалобы». Кнопка «Сообщить о проблеме» в футере →
отправляет URL и
localStorage.userIdв Notifly.
- Дешёвая «Sentry для своих» — никаких лимитов и подписок.
- Реалити-чек после каждого деплоя: пара ошибок — значит зацепили старую часть фронта.
- Контекст по жалобе пользователя — он пишет «у меня не работает», вы открываете Notifly и видите точный URL и стектрейс.
Что улучшить дальше
Заголовок раздела «Что улучшить дальше»- Source maps — загружайте
.map-файлы черезPOST /web-script/:id/sourcemaps, и стектрейсы в уведомлении автоматически раскроются обратно вsrc/file.ts:LINE:COL(подробности — в документации). В CI используйте готовыйscripts/upload-sourcemaps.py. - Хеш стека → дедупликация на стороне Notifly: одинаковые ошибки укрупнять в одно сообщение с счётчиком.
window.NOTIFLY_RELEASE— пропишите git-хэш или семвер, чтобы при деплое сразу видеть, новый ли релиз сломался.
Сравнение подходов
Заголовок раздела «Сравнение подходов»| Критерий | WebScript console_errors | Бэкенд-прокси |
|---|---|---|
| Время интеграции | 2 минуты | 15–30 минут |
| Нужен бэкенд | Нет | Да |
| Sourcemaps | Да (загрузка через REST) | Можно добавить |
| Rate limiting | Встроен (100 req/session) | Свой |
| Подходит для | Pet-проекты, лендинги, MVP | Продакшн с кастомной обработкой |