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 повідомляв про те, що варто знати, а не кричав даремно? Напиши — я налаштовую це для кожного клієнтського проєкту і можу проаудити твій поточний конфіг за одну сесію.