Skip to main content
Назад к блогу
5 мин чтения

Настройка Sentry, которая ловит полезные ошибки (а не шум)

Мой продакшн-конфиг Sentry для Next.js: фильтрация гидрационного спама, группировка по причине и алерты на то, что реально важно. С реальными примерами конфига.

performancenext.js

Свежая установка Sentry на Next.js-приложение генерирует около 400 событий в день на сайте с 2K ежедневных посетителей. 390 из них — шум: гидрационные несовпадения, ошибки браузерных расширений, ResizeObserver-циклы и 404 от prefetch. 10 полезных тонут в потоке.

Я настраивал конфиги Sentry на 6 клиентских проектах. Вот сетап, который я копирую в каждый новый билд.

Шаг 1: Фильтруем шум на входе

Sentry берёт деньги за объём событий. Важнее: шум приучает мозг игнорировать алерты. Убиваем мусор до того, как он попадёт на дашборд.

// sentry.client.config.ts
import * as Sentry from '@sentry/nextjs'

Sentry.init({
  dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
  tracesSampleRate: 0.1, // 10% транзакций — достаточно для перф-данных
  replaysSessionSampleRate: 0,
  replaysOnErrorSampleRate: 0.5, // реплей 50% ошибок

  beforeSend(event) {
    const message = event.exception?.values?.[0]?.value ?? ''

    // Гидрационные несовпадения — проблема React, не твоя
    if (message.includes('Hydration failed')) return null
    if (message.includes('Text content does not match')) return null

    // Браузерные расширения инжектят ошибки
    if (message.includes('chrome-extension://')) return null
    if (message.includes('moz-extension://')) return null

    // ResizeObserver — безвредный, стреляет в каждом браузере
    if (message.includes('ResizeObserver loop')) return null

    // Сетевые ошибки от prefetch/preload — пользователь ушёл со страницы
    if (
      message.includes('Failed to fetch') &&
      event.request?.url?.includes('_next/data')
    ) {
      return null
    }

    return event
  },

  ignoreErrors: [
    // Типичный браузерный шум
    'AbortError',
    'Network request failed',
    'Load failed',
    'cancelled',
    // Боты и краулеры
    'Non-Error promise rejection captured',
  ],

  denyUrls: [
    // Браузерные расширения
    /extensions\//i,
    /^chrome:\/\//i,
    /^moz-extension:\/\//i,
    // Google Translate инжектит сломанные скрипты
    /translate\.googleapis\.com/,
  ],
})

Одно это снижает объём событий на ~85% на типичном Next.js-сайте.

Шаг 2: Серверная фильтрация

Серверный конфиг чище — нет шума от расширений. Но мусор всё равно есть:

// sentry.server.config.ts
import * as Sentry from '@sentry/nextjs'

Sentry.init({
  dsn: process.env.SENTRY_DSN,
  tracesSampleRate: 0.2,

  beforeSend(event) {
    const message = event.exception?.values?.[0]?.value ?? ''

    // Next.js бросает NOT_FOUND как ошибку — это не ошибка
    if (message.includes('NEXT_NOT_FOUND')) return null

    // Next.js redirect() бросает — by design
    if (message.includes('NEXT_REDIRECT')) return null

    return event
  },
})

NEXT_NOT_FOUND и NEXT_REDIRECT — это Next.js, использующий исключения для управления потоком. Это не баги в твоём коде. Фильтрация убирает ~30% серверных событий.

Шаг 3: Кастомные fingerprint-ы для полезной группировки

По умолчанию Sentry группирует ошибки по стэктрейсу. Один и тот же баг создаёт разные issues, если происходит в разных пользовательских флоу. Чиним кастомными fingerprint-ами:

// sentry.client.config.ts — добавляем в beforeSend
beforeSend(event) {
  // ... фильтры шума выше ...

  // Группируем API-ошибки по эндпоинту, не по стэктрейсу
  const frames = event.exception?.values?.[0]?.stacktrace?.frames;
  if (frames?.some(f => f.filename?.includes('/api/'))) {
    const url = event.request?.url ?? 'unknown';
    const path = new URL(url).pathname;
    event.fingerprint = ['api-error', path];
  }

  // Группируем ошибки валидации формы вместе
  if (event.tags?.component === 'ContactForm') {
    event.fingerprint = ['form-error', 'contact'];
  }

  return event;
}

Кастомные fingerprint-ы сократили количество issues на ~40% — один и тот же баг группируется корректно, а не расщепляется на 12 issues.

Шаг 4: Осмысленный контекст на каждой ошибке

Разница между полезной ошибкой и бесполезной — контекст. Добавляем глобально:

// В корневом layout или обёртке приложения
Sentry.setContext('app', {
  locale: currentLocale,
  route: pathname,
  deployment: process.env.VERCEL_GIT_COMMIT_SHA?.slice(0, 7),
})

// Для авторизованных зон
Sentry.setUser({
  id: user.id,
  // Никогда не отправляй PII — ни email, ни имя
})

Когда ошибка срабатывает, я сразу вижу: какая локаль, какой роут, какой деплой и какой пользователь (по ID, не по имени). Этого обычно достаточно, чтобы воспроизвести за 2 минуты вместо 20.

Шаг 5: Алерты, которые будят по делу

Дефолтные алерты Sentry стреляют на каждый новый issue. После деплоя получаешь 15 алертов на мелкие регрессии. Соотношение сигнал/шум обваливается.

Мои правила алертов:

Алерт 1: Обнаружение всплесков — срабатывает, когда количество ошибок превышает 5× скользящее среднее за любое 15-минутное окно. Это ловит реальные аутейджи, а не единичные ошибки.

Алерт 2: Новый issue в платежах — любая новая ошибка в файлах, совпадающих с */api/payment* или */stripe*. Платежи — единственная область, где одна ошибка имеет значение.

Алерт 3: Процент необработанных rejection — срабатывает, когда unhandled rejection превышают 1% сессий в часовом окне. Это ловит каскадные сбои.

Всё остальное идёт в еженедельный дайджест. Просматриваю в понедельник утром — 5 минут на просмотр, триаж всего, что стоит чинить.

Шаг 6: Source maps (то, что все забывают)

Без source maps стэктрейсы Sentry показывают минифицированные имена переменных. Бесполезно. Next.js + Vercel упрощают это, но не автоматически:

// next.config.js
const { withSentryConfig } = require('@sentry/nextjs')

module.exports = withSentryConfig(nextConfig, {
  org: 'your-org',
  project: 'your-project',
  silent: true,
  hideSourceMaps: true, // Не экспозим в браузерных devtools
  widenClientFileUpload: true, // Загружаем все клиентские чанки
})

hideSourceMaps: true критичен — загружает source maps в Sentry при билде, но не раздаёт их публично. Пользователи не могут реверс-инжинирить твой код, но Sentry показывает чистые стэктрейсы.

Результат

До настройки: ~400 событий/день, 3 алерта/день, 95% шума. Я перестал проверять Sentry.

После настройки: ~40 событий/день, ~2 алерта/неделю, каждый actionable. Я снова проверяю Sentry.

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


Хочешь, чтобы твой Sentry сообщал о том, что стоит знать, а не кричал впустую? Напиши — я настраиваю это для каждого клиентского проекта и могу проаудировать твой текущий конфиг за одну сессию.